Jackson don't deserialize properties value - java

I have pojo objects with inheritance and generics like this:
child object:
#Data
#ToString(callSuper = true)
#EqualsAndHashCode(callSuper = true)
public class MessageCreatedEvent extends AbstractEvent<MessageDto> {
#JsonCreator
public MessageCreatedEvent(MessageDto data) {
super(data);
}
}
parent:
#Data
public abstract class AbstractEvent<T> {
private final UUID id = UUID.randomUUID();
private T data;
public AbstractEvent(T data) {
this.data = data;
}
}
and content holding object:
#Data
public class MessageDto implements Serializable {
private UUID id;
private String content;
// and other fields
}
and jackson configuration which is used in rabbitTemplate:
#Bean
public MessageConverter jsonMessageConverter() {
return new Jackson2JsonMessageConverter();
}
At start I didn't use #JsonCreator property but when I receive json message from RabbitMQ and tried it deserialize in rabbit handler I got this error:
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of MessageCreatedEvent (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
After this I added #JsonCreator but then properties in MessageDto object are not set. There is only id field filled and others are null.
Can you tell me what I have wrong configured? Thank you.
EDIT:
I try modified pojos and remove generic data field from parent, and move it into child, now deserialization working, so it looks like that Jackson has som problem with generics. Any idea?

I copied your classes in a sample spring boot app (I am not using Lombok but replicating its behaviour) and doing the following and is working
public class Testing {
public static void main(String[] args) throws
JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
MessageDto data = new MessageDto(UUID.randomUUID(),
"someContent");
MessageCreatedEvent createdEvent = new MessageCreatedEvent();
createdEvent.setData(data);
String json = objectMapper.writeValueAsString(createdEvent);
System.out.println(json);
MessageCreatedEvent fromJson =
objectMapper.readValue(json, MessageCreatedEvent.class);
System.out.println(fromJson.getData().getContent());
System.out.println(fromJson.getData().getId());
}
}
Added a no argument constructor to MessageDto
Added a no argument constructor to AbstractEvent
Removed the following and is empty class:
#JsonCreator
public MessageCreatedEvent(MessageDto data) {
super(data);
}

I'm not sure if it can help, but you may need to use #JsonProperty. To be more specific, please refer to https://thepracticaldeveloper.com/2016/10/23/produce-and-consume-json-messages-with-spring-boot-amqp/

Related

How to solve conflicting getter definitions for property in jackson without access to source

I'm getting this error:
HTTP Status 500 - Could not write JSON: Conflicting getter definitions for property "oid"
The problem is that the class has two similar methods:
getOID (deprecated) and getOid
But I cannot modify the class as it's just a dependency.
Is there a way to fix this issue?
If you can not modify POJO you can implement custom serialiser or use MixIn feature.
Assume, your class looks like below:
class Id {
private int oid;
#Deprecated
public int getOID() {
return oid;
}
public int getOid() {
return oid;
}
public void setOid(int oid) {
this.oid = oid;
}
#Override
public String toString() {
return "oid=" + oid;
}
}
You need to create an interface with extra configuration:
interface IdIgnoreConflictMixIn {
#JsonIgnore
int getOID();
#JsonProperty
int getOid();
}
Now, you need to register this interface:
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
public class JsonMixInApp {
public static void main(String[] args) throws IOException {
Id id = new Id();
id.setOid(1);
ObjectMapper mapper = new ObjectMapper();
mapper.addMixIn(Id.class, IdIgnoreConflictMixIn.class);
mapper.writeValue(System.out, id);
}
}
Above code prints:
{"oid":1}
See also:
Custom Jackson Serializer for a specific type in a particular class
What is equivalent code settings for #JSonIgnore annotation?
Jackson parse json with unwraping root, but without ability to set #JsonRootName
Make Jackson serializer override specific ignored fields

JSON deserialization to Enum with Java 8, Spring Boot Starter 2.1.9 and Lombok 1.18.10

I'm trying to deserialize to a enumeration but the JSON values (lowercase) differs from the enumeration constants (uppercase).
This is the enumeration:
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Getter;
#Getter
#AllArgsConstructor
public enum ContractTypes {
#JsonProperty("product")
PRODUCT("product"),
#JsonProperty("service")
SERVICE("service");
private String value;
}
As you can see, I have annotated the elements with the #JsonPropertyannotation to try to map the provided value to the suitable constant.
I've also tryied to annotated the attribute value with a #JsonValue annotation. In both cases I obtain the same result:
Field error in object 'createContractRequestDto' on field 'contractType': rejected value [product]; codes [typeMismatch.createContractRequestDto.contractType,typeMismatch.contractType,typeMismatch.enm.ContractTypes,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [createContractRequestDto.contractType,contractType]; arguments []; default message [contractType]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'enm.ContractTypes' for property 'contractType'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [enm.ContractTypes] for value 'product'; nested exception is java.lang.IllegalArgumentException: No enum constant enm.ContractTypes.product]]
Why is not working the #JsonProperty and #JsonValue annotations? How must I code the solution to map the JSON value to the suitable enumeration element?
We can also create a custom converter.
public class ContractTypesConverter implements Converter<String, ContractTypes> {
#Override
public ContractTypes convert(String source) {
return ContractTypes.valueOf(source.toUpperCase());
}
}
this can be further written as like this (Thanks to lambda)
Converter<String, ContractTypes> converter = source -> ContractTypes.valueOf(source.toUpperCase());
And Register it with WebMvcConfigurer like this
#Configuration
public class WebConfiguration implements WebMvcConfigurer {
// ... other configurations
#Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new ContractTypesConverter());
}
}
This worked for me. Not sure what you have missed in your code.
#Getter
public enum ContractTypes {
PRODUCT("product"),
SERVICE("service");
private String value;
ContractTypes(String value){
this.value = value;
}
#JsonValue
public String getValue() {
return value;
}
}
#NoArgsConstructor
#Setter
#Getter
static class Holder {
private ContractTypes contractTypes;
}
#Test
public void test() throws IOException {
Holder holder = new ObjectMapper().readValue("{\"contractTypes\":\"product\"}", Holder.class);
assertEquals(ContractTypes.PRODUCT, holder.contractTypes);
}
Those annotations work on the property not on enum type.
You can use enum directly using like PRODUCT or write a Custom Deserializer and use that for deserialization annotating on property.
#JsonDeserialize(using = ContractTypesDeserializer.class)
ContractTypes contractTypes;
Customer Deserializer implementation
public class ContractTypesDeserializer extends StdDeserializer<ContractTypes> {
private static final long serialVersionUID = -4714891596189L;
public ContractTypesDeserializer() {
super ContractTypes.class);
}
protected ContractTypesDeserializer(Class ContractTypes> type) {super(type);}
#Override
public ContractTypes deserialize(JsonParser parser, DeserializationContext context)
throws IOException, JsonProcessingException {
return ContractTypes.valueOf(parser.getText().toUpperCase());
}
}

Writing custom deserializer for polymorphic type hierarchy Jackson

I'm experimenting with Jackson deserialization for inheritance in Java.
I've a base class:
#Getter //Lombok #Getter
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", include = JsonTypeInfo.As.PROPERTY)
#JsonSubTypes(value = {
#JsonSubTypes.Type(value=ClassA.class, name = "classA")
})
public abstract class BaseClass {
private List<String> fields;
#JsonCreator
public BaseClass(#JsonProperty("fields") final List<String> fields) {
this.fields = fields;
}
}
ClassA is also abstract
#Getter
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "typeA", include = JsonTypeInfo.As.PROPERTY)
#JsonSubTypes(value = {
#JsonSubTypes.Type(value=SubClassA.class, name = "subclassA")
})
public abstract class ClassA extends BaseClass{
private String mode;
#JsonCreator
public ClassA(#JsonProperty("fields") final List<String> fields, #JsonProperty("mode") String mode) {
super(fields);
this.mode = mode;
}
}
My subClassA:
public class SubClassA extends ClassA {
private String dummyField;
public SubClassA(#JsonProperty("fields") final List<String> fields, #JsonProperty("mode") String mode,
#JsonProperty("dummyField") String dummyField) {
super(fields, mode);
this.dummyField = dummyField;
}
}
If I pass in a JSON of in the following form:
{
"type": "classA",
"typeA": "subclassA",
"mode": "testingMode",
"fields": ["1", "2"],
"dummyField": "dummy"
}
I get an error Cannot construct instance of ClassA (no Creators, like default construct, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
I came across this https://github.com/FasterXML/jackson-databind/issues/374 which says this is a known issue with Jackson.
How do I go about writing a customDeserializer for this.
In classA I tried doing this:
#JsonDeserialize(using = ClassADeserializer.class)
and ClassADeserializer is:
public class ClassADeserializer extends StdDeserializer<ClassA> {
private final JsonDeserializer<?> defaultDeserializer;
public ClassADeserializer(JsonDeserializer<?> defaultDeserializer) {
super(ClassA.class);
this.defaultDeserializer = defaultDeserializer;
}
#Override public ClassA deserialize(final JsonParser jsonParser, final DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
return (ClassA) defaultDeserializer.deserialize(jsonParser, deserializationContext);
}
which obviously doesn't work. How do I go about writing a custom deserializer for this?
Problem:
You pass in json "type": "classA",... That means jackson first try to create instance of ClassA..During deserialization jackson search #JsonCreator constructor first..If #JsonCreator missing or can not call #JsonCreator constructor then jackson create object with default constructor and call setter method... In your ClassA #JsonCreator constructor with 2 arguments but jackson call with 3 arguments.. So its fail. then jackson call default constructor to create instance. but default constructor also missing.. thats why u get this error: Cannot construct instance of ClassA (no Creators, like default construct, exist)..
Solution:
As you want to deserialize to SubClassA... You need to use #JsonCreator in SubClassA...Then you need to use #JsonIgnoreProperties to ignore properties type so that jackson create instance of SubClassA instead of ClassA....
Try with below SubClassA:
#Getter
#JsonIgnoreProperties(ignoreUnknown = true)
public class SubClassA extends ClassA {
private String dummyField;
#JsonCreator
public SubClassA(#JsonProperty("fields") final List<String> fields, #JsonProperty("mode") String mode,
#JsonProperty("dummyField") String dummyField) {
super(fields, mode);
this.dummyField = dummyField;
}
}

Genson 1.4 (JSON) not processing inherited Pojo's

I am using genson 1.4 for JSON processing in my REST implementation , JSON Inheritance is not working while using genson .please find the sample code structure below.
This is my BaseObject
This is my BaseObject
public class SynBaseObject implements Serializable
{
private Long status;
//GettersAndSetters
}
This is my Child Class
public class PhoneNumber extends SynBaseObject
{
private String countryCode;
private String areaCode;
private String localNumber;
//GettersAndSetters
}
This is my Response Object
public class ResponseObject implements Serializable
{
private Integer errorCode;
private String errorMessage;
private Long primaryKey;
private SynBaseObject baseClass;
public ResponseObject()
{
}
public SynBaseObject getBaseObject()
{
return baseClass;
}
public void setBaseObject(SynBaseObject baseClass)
{
this.baseClass = baseClass;
}
public Integer getErrorCode()
{
return errorCode;
}
public void setErrorCode(Integer errorCode)
{
this.errorCode = errorCode;
}
}
This is the GENSON JSON Output:
{"baseObject":{"status":null},"errorCode":null,"errorMessage":null,"primaryKey":null}
CountryCode,areaCode and localNumber is missing in JSON,only the base class is processed .Tried the same from code like this
Genson genson = new Genson();
PhoneNumber number = new PhoneNumber();
number.setCountryCode("2");
number.setAreaCode("3");
number.setLocalNumber("9645");
ResponseObject responseObject = new ResponseObject();
responseObject.setBaseObject(number);
String serialize = genson.serialize(responseObject);
System.out.println(serialize);
Output was the same like in the rest service.
By default Genson uses the static type during ser/de. Meaning here it will see the object as an instance of SynBaseObject and not of the concrete type PhoneNumber.
You can tell Genson to use the runtime type via configuration:
Genson genson = new GensonBuilder().useRuntimeType(true).create();
Here you can find some examples on how to customize Genson with Jaxrs.
Note that if you ever want to deserialize to a ResponseObject, then you will probably have troubles as in the json there is no information about what is the concrete type of base object. However if the consumed json is also produced by Genson you can easily solve this problem by enabling class metadata serialization builder.useClassMetadata(true).
Some more documentation about the handling of polymorphic types in Genson.

Jackson ignore all properties of superclass from external library

I am developing using an ORM where I extend a base orm class to create tables.
For example:
public class Person extends DbItem {
#JsonIgnore
private String index;
private String firstName;
private String lastName;
}
Problem is that when I use ObjectMapper to serialize, it tries to serialize the members of the DbItem class. Is there any simple way to prevent this? For example with an annotation.
I had a look at a similar problem Jackson serialization: how to ignore superclass properties but I was hoping it could be done simpler, and I'm not sure if I could do it as I can't change the superclass since it is in an external library.
You can use a Mix-in or #JsonIgnoreProperties
For the purposes of these examples, the base ORM class and extension are assumed to be:
public class DbItem {
public String dbPropertyA;
public String dbPropertyB;
}
and
public class Person extends DbItem {
public String index;
public String firstName;
public String lastName;
}
respectively.
Using a Mix-in
A Mix-in is an abstraction of the de/serialization instructions that Jackson understands from an object itself. It is a way to customize de/serialization of 3rd party classes. In order to define a Mix-in, an abstract class must be created and registered with the ObjectMapper.
Example Mix-in Definition
public abstract class PersonMixIn {
#JsonIgnore public String dbPropertyA;
#JsonIgnore public String dbPropertyB;
#JsonIgnore public String index;
}
Registering the Mix-in
#Test
public void serializePersonWithMixIn() throws JsonProcessingException {
// set up test data including parent properties
Person person = makeFakePerson();
// register the mix in
ObjectMapper om = new ObjectMapper()
.addMixIn(Person.class, PersonMixIn.class);
// translate object to JSON string using Jackson
String json = om.writeValueAsString(person);
assertFalse(json.contains("dbPropertyA"));
assertFalse(json.contains("dbPropertyB"));
assertFalse(json.contains("index"));
System.out.println(json);
}
#JsonIgnoreProperties
If you want to avoid creating a class and configuring the ObjectMapper, the #JsonIgnoreProperties annotation can be utilized. Simply annotate the class you are serializing and list the properties to exclude.
Example Serializable Object
#JsonIgnoreProperties({"index", "dbPropertyA", "dbPropertyB"})
public class Person extends DbItem {
public String index;
public String firstName;
public String lastName;
}
See It In Action
#Test
public void serializePersonWithIgnorePropertiesAnnotation() throws JsonProcessingException {
// set up test data including parent properties
Person person = makeFakePerson();
ObjectMapper om = new ObjectMapper();
// translate object to JSON string using Jackson
String json = om.writeValueAsString(person);
assertFalse(json.contains("dbPropertyA"));
assertFalse(json.contains("dbPropertyB"));
assertFalse(json.contains("index"));
System.out.println(json);
}
You want to do custom field level serialization. This will be a bit more work to maintain your code base, but is by far the simplest solution. See Jackson JSON custom serialization for certain fields for implementation details.

Categories