Deserializing abstract generic class in Java with Jackson - java

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.

Related

#Accessors(fluent = true) doesnot work with Jakson

In Spring boot application with Lombok, I have pojo class AccountDTO
#Data
#Builder
#Accessors(fluent = true)
public class AccountDTO implements Serializable {
private String identification;
}
My project compiles fine. However, it throws an exception in its execution
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No
serializer found for class AccountDTO and no properties discovered to create
BeanSerializer
if I removed the annotation #Accessors(fluent = true), then it will work fine without any problems.
How can i make Lombok #Accessors(fluent = true) and Jackson work together ?
I stumbled on this question recently while encountering the same problem and the solution for me was to add and explicit #JsonProperty annotation on the getter generated by Lombok using:
#Getter(onMethod = #__(#JsonProperty)).
In your case, the class would look like this:
#Data
#Builder
#Accessors(fluent = true)
#Getter(onMethod = #__(#JsonProperty))
public class AccountDTO implements Serializable {
private String identification;
}

Add wrapping to JSON POJO without additional Wrapper classes

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

Jackson unable to deserialize immutable object with enum field

Spring Boot 2.5.4 with Jackson 2.12.4
Given the following simplified enum...
#AllArgsConstructor
#Getter
public enum PaymentMethod {
CREDITCARD(1);
private long id;
}
... and a request object which shall be deserialized using Jackson:
#NoArgsConstructor
#Getter
#Setter
public class PaymentRequest {
#JsonProperty(value = "paymentMethod")
private PaymentMethod paymentMethod;
}
This works just fine. Now, I'd like to make the request object immutable, so I changed it to this:
#RequiredArgsConstructor
#Getter
public class PaymentRequest {
#JsonProperty(value = "paymentMethod")
private final PaymentMethod paymentMethod;
}
But this variant fails:
Cannot construct instance of 'PaymentRequest' (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
Is this some Jackson limitation when deserializing enums?
This issue not because parameter is an Enum, its because Lombok and Jackson doesn't work together in this scenario.
When deserializing to an immutable class, we need to explicitly mention that with Jackson annotation. Usually we annotate the constructor. But in this case the constructor is created by Lombok and Lombok do not add those annotation.
The easy fix is, remove #RequiredArgsConstructor annotation and create constructor yourself. Then annotate constructor as #JsonCreator. Something like this,
#Getter
public class PaymentRequest {
#JsonProperty(value = "paymentMethod")
private final PaymentMethod paymentMethod;
#JsonCreator
public PaymentRequest(PaymentMethod paymentMethod) {
this.paymentMethod = paymentMethod;
}
}

Lombok #SuperBuilder doesn't take parameters

Assuming two simple classes:
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
#EqualsAndHashCode
public class Party {
protected Long id;
protected String status;
}
#Data
#SuperBuilder
#NoArgsConstructor
#AllArgsConstructor
#EqualsAndHashCode(callSuper = true)
public class Person extends Party {
private String name;
private Long sex;
}
The compilation fails on the following error. Upon reading Lombok | #SuperBuilder I have no idea what I could miss.
C:\Dev\companyproject\src\main\java\com\companyproject\entity\Person.java:12
java: type com.companyproject.entity.Party.PartyBuilder does not take parameters
The issue here is the incorrect #Builder annotation on the parent class. The documentation for #SuperBuilder mentions:
Most importantly, it requires that all superclasses also have the #SuperBuilder annotation.
So the correct parent class would be:
#Data
#SuperBuilder // <- annotation replaced here
#NoArgsConstructor
#AllArgsConstructor
#EqualsAndHashCode
public class Party {
protected Long id;
protected String status;
}
Addendum:
A key difference between both annotations is that #SuperBuilder also creates a constructor of the class expecting a corresponding builder argument.
For Party it would look like:
protected Party(PartyBuilder<?, ?> b) {
this.id = b.id;
this.status = b.status;
}
and for Person:
protected Person(PersonBuilder<?, ?> b) {
super(b);
this.name = b.name;
this.sex = b.sex;
}
As you can see, the child class constructor wants to pass its own builder to the parent class constructor and this will only be possible if there is a matching constructor there, and #Builder wouldn't generate it.
Also PersonBuilder<> extends PartyBuilder<>, that is why calling super with the child type builder works fine here.

Jackson Ignores Parent Class properties sent via JSON

I am creating REST Service with spring boot and defined some classes with inheritence, however I'm not able to receive a JSON payload which I am sending from postman to the controller.
JSON Payload which I'm sending :
{
"dummy" : "okok",
"fullName": "okok",
"mobileNumber": 1234567890
}
I am only getting dummy property in the controller, rest of the properties not getting mapped to POJO.
Logging statement prints following line on the console
ownerAccount OwnerAccount(dummy=okok)
I think only OwnerAccount constructor is getting invoked and Account properties not getting initialized.
Please help me understand the missing part or mistake I am doing here.
I have defined following structure :
Account.java
#NoArgsConstructor
#AllArgsConstructor
#Data
public class Account {
#NotBlank(message = "fullName is mandatory")
private String fullName;
#NotNull(message = "mobileNumber is mandatory")
private Long mobileNumber;
#Valid
private AddressRequest addressRequest;
}
OwnerAccount.java
#NoArgsConstructor
#Data
public class OwnerAccount extends Account {
#NotBlank(message = "dummy is mandatory")
private String dummy;
}
OwnerController.java
#RestController
#RequestMapping(path = "/api/v1/account/owner")
public class OwnerAccountResource {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private OwnerAccountService ownerAccountService;
#Autowired
public OwnerAccountResource(OwnerAccountService ownerAccountService) {
this.ownerAccountService = ownerAccountService;
}
#PostMapping("/signup")
public ResponseEntity createOwnerAccount(#RequestBody #Valid OwnerAccount ownerAccountRequest) {
logger.info("ownerAccountDto {}", ownerAccountRequest);
return ResponseEntity.ok(ownerAccountService.createAccount(ownerAccountRequest));
}
}
I suppose you think you are not able to receive dummy field because here you are printing only Account fields because #Data add #ToString annotation but it's not printing super class fields
logger.info("ownerAccountDto {}", ownerAccountRequest);
but if you could debug that controller you would see dummy field is there. You need to override toString() to log dummy field or just add lombok annotation
#NoArgsConstructor
#Data
#ToString(callSuper = true)
public class OwnerAccount extends Account {
#NotBlank(message = "dummy is mandatory")
private String dummy;
}
#ToString(callSuper = true) will include fields from super class

Categories

Resources