How does jackson implement json transfer to object - java

I'm trying to test jackson. I have a class that no setters and no constructors with params(only have a default constructor and getters).
Code like the following:
public class BeanA {
private int attr1;
private int attr2;
public BeanA(){
}
public int getAttr1(){
return attr1;
}
public int getAttr2(){
return attr2;
}
}
I used jackson to transfer json to BeanA.jackson can work well.
I can't understand jackson how to assign values without setter and constructor.

Using reflection, by assigning to the fields directly.
Example of what it's doing internally:
Object o = BeanA.class.newInstance();
Field attr1 = BeanA.class.getDeclaredField("attr1");
attr1.setAccessible(true);
attr1.set(o, 42);
System.out.println(((BeanA) o).getAttr1());

As mentioned by #JB User there are various configuration & defaults Jackson apply for serialization/deserialization. Have look at the below links
http://wiki.fasterxml.com/JacksonFeaturesSerialization and
http://wiki.fasterxml.com/JacksonFeatureAutoDetect
If you override the Feature "CAN_OVERRIDE_ACCESS_MODIFIERS" to false you will get the error you expected.

Related

Jackson deserialization behaviour for a getter without property

While using jackson to deserialize and serialize a java DTO came across a weird behaviour.
Consider this sample java dto,
public class TempClz {
private List<String> field1;
public List<String> getField1() {
return field1;
}
public void setField1(List<String> field1) {
this.field1 = field1;
}
public List<String> getNotAtAll() {
return field1;
}
}
Now on creating a object of this class with field1 set to some value and on serializing this via a standard jackson mapper the sample json string obtained was
{"field1":["123"],"notAtAll":["123"]}
This behaviour was weird and I did not get a direct explanation for this in docs, but that once a getter is made, the property is available for both serialization and deserialization. This created a property using the function name in the resultant json response.
But again on deserialization of this string to pojo, the notAtAll list got appended to field1 list, i.e. in the resultant java object, field1 had size of two with values ["123","123"] which just seems wrong. Fixed this behaviour using jsonIgnore on the getter but can someone please help explain this behaviour exactly and whether this is intended?
A Getter Makes a Non-Public Field Serializable and Deserializable. So, no doubt that it added notAtAll field in json for getNotAtAll method. When you de-serialize the same string it has the values from both the getters but both of them return the same field i.e. field1. And consequently the values are added to your list. You've rightly used #JsonIgnore to ignore this getter.

Jackson ObjectMapper returns null when Object variable is set to Private

I am given this escaped JSON
"{\"UniqueId\":[],\"CustomerOffers\":{},\"Success\":false,\"ErrorMessages\":[\"Test Message\"],\"ErrorType\":\"GeneralError\"}"
and I need to convert it to Java object using Jackson.
// https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind
compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.8'
I created the class:
public class Data {
private List<UUID> UniqueId;
private Map<Integer, List<Integer>> CustomerOffers;
private Boolean Success;
private List<String> ErrorMessages;
private String ErrorType;
// getter, setters
}
Then I created the method to convert it
public class Deserializing {
public void processing(String input) {
ObjectMapper mapper = new ObjectMapper();
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
String jsonInString = "\"{\"UniqueId\":[],\"CustomerOffers\":{},\"Success\":false,\"ErrorMessages\":[\"Test Message\"],\"ErrorType\":\"GeneralError\"}\"";
String newJSON = org.apache.commons.lang3.StringEscapeUtils.unescapeJava(jsonInString);
newJSON= newJSON.substring(1, jsonInString.length()-1);
try {
// JSON string to Java object
Data data = mapper.readValue(newJSON, Data.class);
System.out.println(data);
System.out.println("Get Success "+ data.getSuccess()); // return "false" if Data object is public ; null if private
System.out.println("Get UniqueID " + data.getUniqueId()); // return [] if Data object is public ; null if private
} catch (IOException e) {
e.printStackTrace();
}
}
}
Whichever variables in Data class that are set to public, then I will get the corresponding value when I call getters.
Whichever variables in Data class that are set to private, then I will get null when I call getters.
Getters and Setters are always public.
I am wondering, why ObjectMapper can't map the object if it is set to private? I could set it to public, but that is not best practice.
The issue is that Jackson will always assume setSuccess() & getSuccess() will be used for a success field, not Success. JSON field names starting with uppercase letters need to be supported by #JsonProperty. Java has a convention where class members always start with lowercase letters, you can realize that by using this annotation.
When you make fields private, you force Jackson to utilize setters, and the above conflict makes it impossible to properly deserialize the Data object.
Solution is to do;
public class Data {
#JsonProperty("UniqueId")
private List<UUID> uniqueId;
#JsonProperty("CustomerOffers")
private Map<Integer, List<Integer>> customerOffers;
#JsonProperty("Success")
private Boolean success;
#JsonProperty("ErrorMessages")
private List<String> errorMessages;
#JsonProperty("ErrorType")
private String errorType;
// getter&setters
}
Then you will see the values deserialized properly into a Java object;
Get success false
Get uniqueID []
By default, Jackson will only attempt to serialize public fields on the Data class (or whatever class you are trying to serialize/unserialize). However, you may configure ObjectMapper to allow it serialize all fields, regardless of visibility:
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
Data data = mapper.readValue(newJSON, Data.class);
See here and here for more information.
In the JavaBeans specification (the universal guide for structuring Java class properties), properties are defined as starting with a lower-case letter (errorMessages); the accessors, because they have a prefix, capitalize it (getErrorMessages). Jackson uses the property names by default, so when you have a method getErrorMessages, it looks for a JSON property with the key errorMessages.
The best approach is to change your JSON; I've seen the styles errorMessages and error_messages but never ErrorMessages. If you can't do that, you can apply #JsonProperty to your properties to tell Jackson to use a different name in the JSON.
Also, another point to note : In this if class Data contains any constructor with arguments and doesn't contain an empty constructor. Then the object mapper will not be able to map the corresponding object. So in this case also it will return null.

How to parse DTO to Pojo objects

Well, I'm trying to parse objects and I'm having so much issues.
My classes are like this:
-Entidad-
public class Entidad{
private Long codEntidad;
private Set<Comunicacion> comunicacion;
/*------------ Getter and Setters --------------*/
}
-Comunicacion-
public class Comunicacion {
private Entidad entidad;
private Long codComunicacion;
/*------------ Getter and Setters --------------*/
}
I need to parse to DTO objects:
-EntidadDTO-
public class EntidadDTO{
private Long codEntidad;
private Set<ComunicacionDTO> comunicacionDto;
/*------------ Getter and Setters --------------*/
}
-ComunicacionDTO-
public class ComunicacionDTO {
private EntidadDto entidadDto;
private Long codComunicacion;
/*------------ Getter and Setters --------------*/
}
I tried to use:
BeanUtils.copyProperties(entidad, entidadDto);
It seems that the parse is success but the property entidadDto.getComunicacionDto(); is a hashMap of Comunicacion (not ComunicacionDTO)
Should I try to make a custom parse with reflection?
Also I'd like to use this to parse more objects with a similar structure.
Thanks!
Try dozer. You can define mappings from bean to bean using it.
http://dozer.sourceforge.net/
Why you want to parse java object and move data to other java object?
Parsing is for unstructured strings not for objects.
Use setters/getters to move data from one object to the other, using reflection will make you cry when you start doing refactorings.

Deserialize JSON to class with abstract and non-default constructor types of fields

I am working on conversion from JSON using Jackson 2, I work with application code that I cannot interfere into (Tacton Software). In there I have a class like that:
public class Quotation {
private Reference currency; // This type does not have deafult constructor
private List<QuotationItem> quotationItems = new ArrayList(); // In this there are abstract fields
// Getters, setters and other fields omitted for clarity
}
So basically I tried already to extend Quotation class like that:
class JSONQuotation extends Quotation{
#JsonSetter("currency")
public void notSetCurrency(JSONReference ref){
this.setCurrency(null);
}
#JsonSetter("items")
#Override
public void setItems (List<QuotationItem> item){
}
}
So there are two types of annotation I used:
Ignore setter for field I don't want to deserialize
Override setter
Anyway I'm still getting errors from ObjectMapper, of course, I had set MixIns
mapper.addMixIn(Quotation.class,JSONQuotation.class);
Anybody can help me with this or recommend some other deserializer ?

Want to hide some fields of an object that are being mapped to JSON by Jackson

I have a User class that I want to map to JSON using Jackson.
public class User {
private String name;
private int age;
private int securityCode;
// getters and setters
}
I map this to a JSON string using -
User user = getUserFromDatabase();
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(user);
I don't want to map the securityCode variable. Is there any way of configuring the mapper so that it ignores this field?
I know I can write custom data mappers or use the Streaming API but I would like to know if it possible to do it through configuration?
You have two options:
Jackson works on setters-getters of fields. So, you can just remove getter of field which you want to omit in JSON. ( If you don't need getter at other place.)
Or, you can use the #JsonIgnore annotation of Jackson on getter method of that field and you see there in no such key-value pair in resulted JSON.
#JsonIgnore
public int getSecurityCode(){
return securityCode;
}
Adding this here because somebody else may search this again in future, like me. This Answer is an extension to the Accepted Answer
You have two options:
1. Jackson works on setters-getters of fields. So, you can just remove getter of field which you want to omit in JSON. ( If you don't need getter at other place.)
2. Or, you can use the `#JsonIgnore` [annotation of Jackson][1] on getter method of that field and you see there in no such key-value pair in resulted JSON.
#JsonIgnore
public int getSecurityCode(){
return securityCode;
}
Actually, newer version of Jackson added READ_ONLY and WRITE_ONLY annotation arguments for JsonProperty. So you could also do something like this.
#JsonProperty(access = Access.WRITE_ONLY)
private String securityCode;
instead of
#JsonIgnore
public int getSecurityCode(){
return securityCode;
}
you also can gather all properties on an annotation class
#JsonIgnoreProperties( { "applications" })
public MyClass ...
String applications;
If you don't want to put annotations on your Pojos you can also use Genson.
Here is how you can exclude a field with it without any annotations (you can also use annotations if you want, but you have the choice).
Genson genson = new Genson.Builder().exclude("securityCode", User.class).create();
// and then
String json = genson.serialize(user);
Field Level:
public class User {
private String name;
private int age;
#JsonIgnore
private int securityCode;
// getters and setters
}
Class Level:
#JsonIgnoreProperties(value = { "securityCode" })
public class User {
private String name;
private int age;
private int securityCode;
}
if you are using GSON you have to mark the field/member declarations as #Expose and use the GsonBuilder().excludeFieldsWithoutExposeAnnotation().create()
Don't forget to mark your sub classes with #Expose otherwise the fields won't show.
I suggest you use this.
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private int securityCode;
This allows you to set the value of securityCode(especially if you use lombok #Setter) and also prevent the field from showing up in the GET request.
I had a similar case where I needed some property to be deserialized (JSON to Object) but not serialized (Object to JSON)
First i went for #JsonIgnore - it did prevent serialization of unwanted property, but failed to de-serialize it too. Trying value attribute didn't help either as it requires some condition.
Finally, working #JsonProperty with access attribute worked like a charm.

Categories

Resources