Add wrapping to JSON POJO without additional Wrapper classes - java

I want to communicate with another application via REST that uses JSON with multi-layer wrapping.
e.g.:
I have the following POJO class:
#XmlRootElement
#AllArgsConstructor
#NoArgsConstructor
public class Message {
#Getter
#Setter
#XmlElement(name="meta")
private WrapperMeta metaParameters;
#Getter
#Setter
#XmlElement(name="message")
private WrapperMessage messageParameters;
}
the Wrapper class:
#Getter
#Setter
#XmlElement(name="parameters")
private MetaParameters meta;
}
the JSON generated from it:
{
"meta": {
"parameters": {
"service": "some",
"sender": {
"id": "2",
"name": "Jane Doe"
}
}
},
"message": {
"parameters": {
"message": "hi"
"sent": 1630597537
}
}
}
I want to get rid of the Wrapper classes, and instead have a more elegant solution.
e.g., with Annotations, like this:
#XmlRootElement
#AllArgsConstructor
#NoArgsConstructor
public class Message {
#Getter
#Setter
#XmlElementWrapper(name="meta")
#XmlElement(name="parameters")
private MetaParameters metaParameters;
#Getter
#Setter
#XmlElementWrapper(name="message")
#XmlElement(name="parameters")
private MessageParameters message;
}
But the XmlElementWrapper annotation only seems to add the wrapping for XML, not JSON.

Replace your #XmlElement with #JsonProperty. Like this,
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
public class Message {
#JsonProperty("meta")
private WrapperMeta metaParameters;
#JsonProperty("message")
private WrapperMessage messageParameters;
}
Change your WrapperMessage class like this.
#Getter
#Setter
public class WrapperMessage {
#JsonUnwrapped
private MetaParameters meta;
}
Note: You can add Getter and #Setter in class level. Like above examples

Related

Map<classObject,List> is not formed as expected

I have the below json, which i need to read and construct as Map<NamePlateOrder, List<Orders>>
Sample json:
`{
"orders":[
{
"order":[
{
"OrderReference":{
"reference":68203486,
"version":1
}
},
{
"OrderReference":{
"reference":68203487,
"version":1
}
}
],
"nameplate":{
"id":"98ZZ",
"label":"VEC"
}
}
]
}`
Below is the code:
public class NamePlateOrder {
public ArrayList<Orders> orders;
#EqualsAndHashCode
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
#Builder
public static class NameplateCatalogForOrder {
public String id;
public String label;
}
#EqualsAndHashCode
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
#Builder
public static class Orders {
public List<Order> order; //this should be stored as value.
public NameplateCatalogForOrder nameplate; //this should be stored as key
}
#EqualsAndHashCode
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
#Builder
public static class Order {
#JsonProperty("OrderReference")
public OrderReference orderReference;
}
#EqualsAndHashCode
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
#Builder
public static class OrderReference {
public int reference;
public int version;
}
}
Sample code tried to construct Map<NameplateCatalogForOrder, List<Orders>>
`NamePlateOrder models = om.readValue(sql2, NamePlateOrder.class);
System.out.println("NNamePlateWithOrder " + models);
Map<NamePlateOrder.NameplateCatalogForOrder, List<NamePlateOrder.Order>> nameplateListMap = new HashMap<>();
for(NamePlateOrder.Orders firstOrders : models.getOrders()){
NamePlateOrder.NameplateCatalogForOrder n1 = firstOrders.getNameplate();
List<NamePlateOrder.Order> ordersListForNamePlate = firstOrders.getOrder();
nameplateListMap.put(n1, ordersListForNamePlate);
}`
But the output when executed the above code is not as expected. The key n1 is printing as object reference. The expected output should be same as the sample json which we are reading to construct the same.
Any inputs would be helpful.
#EqualsAndHashCode
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
#Builder
public static class Order {
This christmas tree does not include #ToString, hence, the toString() implementation of these things would print something like Order#12af9cc, which I think is what you mean with 'prints its object reference'. In other words, your code is doing exactly what you asked it to.
I suggest you use #Value #Builder instead and ditch the setters. It's a bit odd to want a builder for a mutable class, after all (why not just invoke the setters instead?). If you must have them, #NoArgsConstructor #AllArgsConstructor #Value #Data works too. #Data combines #EqualsAndHashCode #Getter #Setter #ToString which is what you want.

Deserializing abstract generic class in Java with Jackson

I am sending a POST request with the Sample JSON request body:
"name" : "jeff",
"country" : "US",
"content" : {
"subject" : "Test-Subject",
"body" : "Test-body"
}
The class that is this JSON is deserialized into:
#Introspected
#Builder
#Data
public class Template<T extends Content> {
String name;
String country;
T content;
}
Content looks like this:
#Introspected
#Superbuilder
#Getter
#EqualsAndHashCode
#NoArgsConstructor
#AllArgsConstructor(onConstructor_ = #JsonCreator)
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME, property="content")
#JsonSubTypes(#JsonSubTypes.Type(value = EmailContent.class, name="EmailContent"))
public abstract class Content {
private String body;
}
This is what I want T content to deserialize into:
#Introspected
#Superbuilder
#Getter
#EqualsAndHashCode(callSuper = true)
#NoArgsConstructor
#AllArgsConstructor(onConstructor_ = #JsonCreator)
public class EmailContent extends Content {
private String subject;
}
I have everything working in Template EXCEPT the generic content type which is giving me trouble no matter what JsonTypeInfo I use. I am trying to deserialize it into an EmailTemplate class. I have other classes extending from content so I am not looking to use #JsonDeserialize.
Solved...it was due to using graalVM which supports partial reflection. I fixed this by adding the #ReflectiveAccess annotation alongside lombok's #Jacksonized annotation for deserializing builder types.

Mapping DTO to specific subclass of parent entiy using Spring JPA where InheritanceType.TABLE_PER_CLASS

I am trying to map DTO to the corresponding #Entity in the service layer.
Condition may be of a set of types: Amount, Title, Date. Each condition, except Amount, has a unique predefined set of clauses.
TitleCondition: includes, startsWith
DateCondition: from, until
The idea is to use common Condition entity with #Inheritance(strategy= InheritanceType.TABLE_PER_CLASS).
The 2 problems I see with this code is:
It is unclear how to properly set data
data type is Object
Is there a way to use convenient Lombok's #Builder with given mapping? What would be the simpler and better way to map dto to entity?
Service:
#Service
public class FilterService {
private Condition convertConditionDtoToEntity(ConditionDto conditionDto) {
Type type = typeRepository.findFirstByName(conditionDto.getType())
.orElseThrow(UnsupportedOperationException::new);
Clause clause;
if (conditionDto.getClause() != null) {
clause = clauseRepository.findFirstByName().orElseThrow(UnsupportedOperationException::new);
}
if (conditionDto.getType().equals("amount")) {
return AmountCondition.builder().type(type).data(???).build();
} else if (conditionDto.getType().equals("title")) {
return TitleCondition.builder().type(type).clause(clause).data(???).build();
} else if (conditionDto.getType().equals("date")) {
return DateCondition.builder().type(type).clause(clause).data(???).build();
} else {
throw new UnsupportedOperationException();
}
}
}
Condition
#Getter
#SuperBuilder
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Inheritance(strategy= InheritanceType.TABLE_PER_CLASS)
public abstract class Condition {
#Id
#GeneratedValue
private long id;
#ManyToOne
private Filter filter;
#Getter
#ManyToOne
public Type type;
public abstract Object getData();
}
DateCondition
#Data
#SuperBuilder
#NoArgsConstructor
#AllArgsConstructor
#Entity
public class DateCondition extends Condition {
#Column
int clauseId;
#Column
#Temporal(TemporalType.DATE)
Date date;
#Getter
#ManyToOne
private Clause clause;
#Override
public Object getData() {
return date;
}
}
TitleCondition
#Data
#SuperBuilder
#NoArgsConstructor
#AllArgsConstructor
#Entity
public class TitleCondition extends Condition {
#Column
int clauseId;
#Column
String title;
#Getter
#ManyToOne
private Clause clause;
#Override
public Object getData() {
return title;
}
}
AmountCondition
#Data
#SuperBuilder
#NoArgsConstructor
#AllArgsConstructor
#Entity
public class AmountCondition extends Condition {
#Column
int amount;
#Override
public Object getData() {
return amount;
}
}
Clause
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
#Entity
public class Clause {
#Id
#GeneratedValue
private long id;
#Column
String name;
#OneToMany(mappedBy = "clause")
private Set<Type> types = new HashSet();
}
Type
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
#Entity
public class Type {
#Id
#GeneratedValue
private long id;
#Column
String name;
#ManyToOne
private Clause clause;
#OneToMany(mappedBy = "type")
private Set<Condition> conditions;
}
I have two forms that I use a lot and save a lot of time.
first: transform your model into json and convert the json to object the Mapper class (this link will help https://www.baeldung.com/jackson-object-mapper-tutorial)
second: spring has some cool functions about it. an example would be the BeanUtils.copyProperties function (source, target);
data problem: the data field does not exist for this reason you do not need to set it.
Another problem that I was able to notice is that their properties are all defaulted so set them as private.

Jackson #JsonIgnoreProperties isn't working on referenced class property?

I am trying to make a model (Request) class that would ignore a nested class's property it references. And I was told that I cannot modify referenced model (User) in any way, so what I had tried was to ignore its property from the Request model.
#Data
#Builder
public class Request {
#JsonIgnoreProperties(value = {"id"})
User user;
}
// class I cannot modify
#Data
#Builder
public class User {
String id;
String name;
...
}
In the payload, I am still seeing id serialized & deserialized so the annotation clearly doesn't work. It seems to work fine if I place it at the class level of User but since I cannot modify User, I've also tried:
#JsonIgnoreProperties(value = {"user.id"})
public class Request {
User user;
None of above works. The doc is saying
Starting with 2.0, this annotation can be applied both to classes and to properties
I am using Jackson 2.10.2 in a Spring Boot project. What am I missing?
Create a class who extends User as bellow:
public class MyUser extends User {
#JsonIgnore
String id;
}
#Data
#Builder
public class Request {
MyUser user;
}
I'm running this test and works:
#Data
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class Request {
#JsonIgnoreProperties("id")
User user;
}
#Data
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class User {
String id;
String name;
}
public class TestClass {
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper mapper= new ObjectMapper();
final Request value = Request.builder()
.user(User.builder()
.id("qw423432")
.name("asdasdjsadjasdasd")
.build())
.build();
System.out.println(mapper.writeValueAsString(value));
System.out.println(mapper.readValue("{\"user\":{\"id\": \"a\", \"name\":\"b\"}}", Request.class));
}
}
Do you see any difference with your test?

Mapping nested list

I have a object with list of nested object
#Getter
#Setter
#NoArgsConstructor
public class Notification {
private Long id
private Long statusId;
private List <External> external;
}
#Getter
#Setter
#NoArgsConstructor
public class External{
private Long externalId;
private LocalDate date;
}
Dto
#Getter
#Setter
#NoArgsConstructor
public class NotificationPayload {
private Long id;
private Long statusId;
private List <ExternalReferencePayload> external;
}
#Getter
#Setter
#NoArgsConstructor
public class ExternalReferencePayload {
private Long externalReferenceId;
}
Mapper
#Mapper(componentModel = "spring")
public interface NotificationMapper{
public Notification dtoToNotification(NotificationPayload payload);
}
I search the way to map the nested list
In order to perform custom mapping for certain elements it is only needed to define a mapping method and MapStruct will take care of the rest. In your example:
#Mapper(componentModel = "spring")
public interface NotificationMapper{
public Notification dtoToNotification(NotificationPayload payload);
#Mapping(target = "externalId", source = "externalReferenceId")
public External dtoExternal(ExternalReferencePayload payload);
}
With this the nested list will use the dtoExternal mapping method to perform the mapping. With #Mapping you control how the mapping between externalId and externalReferenceId should be done

Categories

Resources