Deserialization of Object serialized by #JsonIdentityInfo - java

I am having my User class annotated like this to remove cyclic format of output:
#Entity
#Table(name = "USER")
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id", scope = User.class)
public class User extends AbstractValueObject {
private Integer id;
private String name;
.....
}
public class Load extends AbstractValueObject {
private Integer id;
private User postedBy;
}
So whenever i fetch List of Load it is giving me output as below JSON :
[
{
"id" : 1,
"postedBy" : {
"id":1,
"name":"SOF"
}
},
{
"id" : 2,
"postedBy" : 1
}
]
But client side wants it in original format - say each object of load should contain full postedBy object. Client side is in Android - Java.
Is there any way at Android end to de-serialize object in original format at Android ?
Expected output :
[
{
"id" : 1,
"postedBy" : {
"id":1,
"name":"SOF"
}
},
{
"id" : 2,
"postedBy" : {
"id":1,
"name":"SOF"
}
}
]
I tried with JSOG but in some cases it fails.
Any help will be appreciated. :)

You can use Jsog Converter, which provide library to encode and decode objects.
https://github.com/jsog/jsog

Related

Aggregation Java MongoDB with MongoTemplate not return group value

In my Spring Controller I cannot figure out how to retrieve correctly the result of a MongoDB with MongoTemplate aggregation. Below is the code from my Controller class:
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.match(Criteria.where("modality").is("check")),
Aggregation.group("name").count().as("check_count"),
Aggregation.project("name").and("check_count").as("check_count")
);
AggregationResults<Response> res = mongoTemplate.aggregate(aggregation, "user", Response.class);
Below the simple class of User and Response:
#Document(collection = "user")
class User{
#Id
private String id;
private String name;
private String modality;
//constructor and get/set
}
class Response{
private String name;
private string check_count;
//constructor and get/set
}
So I retrieve correctly my response but I do not see the name, that is always null:
{
"user": [
{
"name": null,
"check_count": 61
},
{
"name": null,
"check_count": 15
},...
What is wrong in my Aggregation.group ? Thanks
When you do the group aggregation, the pipeline that gets generated is mostly
{ "$group" : { "_id" : "$name", "check_count" : { "$sum" : 1}}}
So, the result of the group stage will have _id as the field, not name.
Subsequent, stage should use _id field.
Aggregation.project().and("_id").as("name").and("check_count").as("check_count");

Grouping in Spring Data MongoDB returns NULL _id when mapping to a class with composite key

I want to aggregate a collection of documents that match certain conditions, group them and map the output to a different class object. The aggregation works fine and I get the expected total but the _id field is always NULL.
I'm using spring-data-mongodb 2.1.11 and MongoDB 3.6.
This is the class to be aggregated:
#Document
public class LegOrder {
public static class Key {
#Indexed
long itemId;
long transactionId;
...
}
#Id
private Key id;
#Indexed
private long brandId;
private int units;
...
}
This is the aggregation output class:
#Document
public class ItemAggregation {
public static class Key {
#Indexed
long itemId;
#Indexed
long brandId;
}
#Id
private Key id;
private long total;
...
}
My aggregation method:
public ItemAggregation aggregate(long itemId, long brandId) {
MatchOperation matchStage = Aggregation.match(new Criteria().andOperator(
Criteria.where("id.itemId").is(itemId),
Criteria.where("brandId").is(brandId)
));
GroupOperation groupStage = Aggregation.group("id.itemId", "brandId")
.sum("units").as("total")
...
;
Aggregation aggregation = Aggregation.newAggregation(matchStage, groupStage);
return mongoTemplate.aggregate(aggregation, LegOrder.class, ItemAggregation.class).getUniqueMappedResult();
}
The executed query in MongoDB:
[
{
"$match": {
"$and": [
{ "_id.itemId": 1 },
{ "brandId": 2}
]
}
},
{
"$group": {
"_id": {
"itemId": "$_id.itemId",
"brandId": "$brandId"
},
"total": { "$sum": "$units" }
}
}
]
If I run this query in the mongo shell the _id field is properly populated.
Any idea how to achieve it?
Thank you
Sorry for the late response. I faced this issue now and found this solution.
My aggregation output in console is
{
"_id" : {
"ownerId" : BinData(3,"xkB0S9Wsktm+tSKBruv6og=="),
"groupbyF" : "height"
},
"docs" : [
{
"id" : ObjectId("5fe75026e211c50ef5741b31"),
"aDate" : ISODate("2020-12-26T15:00:51.056Z"),
"value" : "rrr"
}
]
}
{
"_id" : {
"ownerId" : BinData(3,"AAAAAAAAAAAAAAAAAAAAAA=="),
"groupbyF" : "weight"
},
"docs" : [
{
"id" : ObjectId("5fe6f702e211c50ef5741b2f"),
"aDate" : ISODate("2020-12-26T08:40:28.742Z"),
"value" : "55"
},
{
"id" : ObjectId("5fe6f6ade211c50ef5741b2e"),
"aDate" : ISODate("2020-12-26T08:38:58.098Z"),
"value" : "22"
}
]
}
The mapping that worked for me
import lombok.Data;
#Data
public class AggregationLatest2Type{
private String ownerId;
private String key;
private List<Doc> docs;
#Data
public class Doc{
private String _id;
private Date aDate;
private String value;
}
}

Deserialize Map key as POJO reference

Is it possible in Jackson to get a reference to a previously-deserialized object from the same json string given it's id?
For example, I have the following Java classes:
public class Company {
List<Employee> employeeList;
List<Customer> customerList;
....
}
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class Customer {
Long id;
...
}
public class Employee {
Long id;
Map<Customer, int> utilityMap;
...
}
I want to create a Company instance from a json string that looks like:
{
"customerList" : [ {
"id" : 0,
"name" : "customer0"
} ],
"employeeList" : [ {
"id" : 1,
"fullName" : "employee0",
"utilityMap" : {
"0" : 1
}
} ]
}
where the key 0 in utilityMap refers to the customer with id 0.
With KeyDeserializer I'm able to create a new Customer object, but I want the Map's key to be a reference to the corresponding object instead.
A simple solution would be to change the signature of Map<Customer, int> to be Map<Long, int>, but this not possible due to internal dependencies, is there another way to accomplish this without changing the model (similar to xml references)?

How to create xml webservice DTOs depending on conditions?

Is it possible to create a global DTO for an xml webservice, but having conditional fields inside?
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#JsonInclude(JsonInclude.Include.NON_EMPTY)
public class MyDTO {
...
#XmlElementWrapper
#XmlElement(name = "somename")
private List<String> list;
}
Now, what if I want to release another version of the webservice, and rename the #XmlElement field thereby (or introduce additional fields, remove some, etc).
So that backwards compatibility is retained, but the same objects are used for the "new" version.
I could maybe do this my adding request path methods with /v1, /v2 etc.
But how could I then maintain a single DTO class, but with fields conditional on the version path?
Or would I always have to duplicate those DTO classes and modify exactly to me needs of the version?
#RestController
public void MyServlet {
#RequestMapping("/v1")
public MyDTO1 request1() {
}
#RequestMapping("/v2")
public MyDTO2 request2() {
}
}
I would prefer using tailored DTOs for each version of the API. To avoid boilerplate code when mapping your entities to DTOs, you could consider using mapping frameworks such as MapStruct.
If you are using Jackson, you could consider using JSON Views (they will work with XML too). Quoting from the Latest Jackson integration improvements in Spring article:
JSON Views
It can sometimes be useful to filter contextually objects serialized to the HTTP response body. In order to provide such capabilities, Spring MVC now has builtin support for Jackson’s Serialization Views (as of Spring Framework 4.2, JSON Views are supported on #MessageMapping handler methods as well).
The following example illustrates how to use #JsonView to filter fields depending on the context of serialization - e.g. getting a “summary” view when dealing with collections, and getting a full representation when dealing with a single resource:
public class View {
interface Summary {}
}
public class User {
#JsonView(View.Summary.class)
private Long id;
#JsonView(View.Summary.class)
private String firstname;
#JsonView(View.Summary.class)
private String lastname;
private String email;
private String address;
private String postalCode;
private String city;
private String country;
}
public class Message {
#JsonView(View.Summary.class)
private Long id;
#JsonView(View.Summary.class)
private LocalDate created;
#JsonView(View.Summary.class)
private String title;
#JsonView(View.Summary.class)
private User author;
private List<User> recipients;
private String body;
}
Thanks to Spring MVC #JsonView support, it is possible to choose, on a per handler method basis, which field should be serialized:
#RestController
public class MessageController {
#Autowired
private MessageService messageService;
#JsonView(View.Summary.class)
#RequestMapping("/")
public List<Message> getAllMessages() {
return messageService.getAll();
}
#RequestMapping("/{id}")
public Message getMessage(#PathVariable Long id) {
return messageService.get(id);
}
}
In this example, if all messages are retrieved, only the most important fields are serialized thanks to the getAllMessages() method annotated with #JsonView(View.Summary.class):
[ {
"id" : 1,
"created" : "2014-11-14",
"title" : "Info",
"author" : {
"id" : 1,
"firstname" : "Brian",
"lastname" : "Clozel"
}
}, {
"id" : 2,
"created" : "2014-11-14",
"title" : "Warning",
"author" : {
"id" : 2,
"firstname" : "Stéphane",
"lastname" : "Nicoll"
}
}, {
"id" : 3,
"created" : "2014-11-14",
"title" : "Alert",
"author" : {
"id" : 3,
"firstname" : "Rossen",
"lastname" : "Stoyanchev"
}
} ]
In Spring MVC default configuration, MapperFeature.DEFAULT_VIEW_INCLUSION is set to false. That means that when enabling a JSON View, non annotated fields or properties like body or recipients are not serialized.
When a specific Message is retrieved using the getMessage() handler method (no JSON View specified), all fields are serialized as expected:
{
"id" : 1,
"created" : "2014-11-14",
"title" : "Info",
"body" : "This is an information message",
"author" : {
"id" : 1,
"firstname" : "Brian",
"lastname" : "Clozel",
"email" : "bclozel#pivotal.io",
"address" : "1 Jaures street",
"postalCode" : "69003",
"city" : "Lyon",
"country" : "France"
},
"recipients" : [ {
"id" : 2,
"firstname" : "Stéphane",
"lastname" : "Nicoll",
"email" : "snicoll#pivotal.io",
"address" : "42 Obama street",
"postalCode" : "1000",
"city" : "Brussel",
"country" : "Belgium"
}, {
"id" : 3,
"firstname" : "Rossen",
"lastname" : "Stoyanchev",
"email" : "rstoyanchev#pivotal.io",
"address" : "3 Warren street",
"postalCode" : "10011",
"city" : "New York",
"country" : "USA"
} ]
}
Only one class or interface can be specified with the #JsonView annotation, but you can use inheritance to represent JSON View hierarchies (if a field is part of a JSON View, it will be also part of parent view). For example, this handler method will serialize fields annotated with #JsonView(View.Summary.class) and #JsonView(View.SummaryWithRecipients.class):
public class View {
interface Summary {}
interface SummaryWithRecipients extends Summary {}
}
public class Message {
#JsonView(View.Summary.class)
private Long id;
#JsonView(View.Summary.class)
private LocalDate created;
#JsonView(View.Summary.class)
private String title;
#JsonView(View.Summary.class)
private User author;
#JsonView(View.SummaryWithRecipients.class)
private List<User> recipients;
private String body;
}
#RestController
public class MessageController {
#Autowired
private MessageService messageService;
#JsonView(View.SummaryWithRecipients.class)
#RequestMapping("/with-recipients")
public List<Message> getAllMessagesWithRecipients() {
return messageService.getAll();
}
}

Serialize List content in a flat structure in jackson json (Java)

The class I need to Serialize:
#JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
public class MyClass {
#JsonProperty("CustomerId")
private String customerId;
#JsonProperty("Products")
private List<ProductDetails> products;
//Getters and setters
}
My ProductDetails.java class:
#JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
public class ProductDetails {
#JsonProperty("ProductId")
private String productId;
#JsonProperty("ProductName")
private String productName;
//Getters and setters
}
The default serialized output:
{
"CustomerId" : "ewm0po",
"Products" : [ {
"ProductId" : "AAA",
"ProductName" : "AAA Product"
}, {
"ProductId" : "AAA",
"ProductName" : "AAA Product"
}]
}
The output I'm trying to get:
{
"CustomerId" : "ewm0po",
"ProductId1" : "AAA",
"ProductName1" : "AAA Product"
"ProductId2" : "AAA",
"ProductName2" : "AAA Product"
}
In other words, I am trying to skip the JSON brackets for the Products-list and suffix each of the ProductId and ProductName fields with a increasing integer.
Any help is very much appreciated!
As #Boris the spider pointed out. Writing a custom serializer was the solution. Not as painful as I expected :-)

Categories