Jackson Deserialize and Serialize with different field name - java

I have a class that I want to deserialize and serialize with Jackson. Currently this is what I have
class Person {
#JsonAlias("fullName")
#JsonDeserialize(using = NameDeserializer.class)
String name;
}
I want to be able to read from {"fullName": "John Doe"} and write to {"name": "John Doe"}.
I am also using Lombok to create setter and getter, so I could not use #JsonProperty on method level. Any idea how would I approach this, because currently it seems like the #JsonAlias is not working as expected. I'm using Jackson 2.10 to map object.
EDIT
Turns out using Alias works just fine. Though in figuring out how it didn't work the first time, I override lombok setter
#JsonProperty("firstName")
public void setName(String name){
this.name = name;
}
From here I found out that my Deserializer is actually the problem.

Just remove the custom deserializer, and annotate the field with #JsonProperty, which should make your scenario work:
class Person {
#JsonAlias("fullName")
#JsonProperty
String name;
}
The custom deserializer controls how the deserialization is done, and this is where the alias is probably being ignored.

you can use JsonProperty annotation with properties as it is written here https://github.com/FasterXML/jackson-annotations
public class Name {
#JsonProperty("firstName")
public String _first_name;
}

Related

default Jackson naming strategy for fields with short names

I've used Jackson for years, and I am not sure I ever faced this issue.
Using Jackson 2.12.5 in a Spring Boot 2.5.5 project, I have an object that I need to serialize. I have no issue with other fields, but these 2 fields are causing me problems :
#Jacksonized
#Builder
#Getter
public class ComputationResult {
private final String pId;
private final String cId;
... other fields ignored
}
As you can see, I am also using Lombok annotations. When "delombokized", the getters are :
public String getPId() {
return this.pId;
}
public String getCId() {
return this.cId;
}
when serializing the POJO, I expect the field names to be "pId" and "cId", but they are not : I get "pid" and "cid", all lower-case. I don't have the problem with other fields, for which the case is respected.
It caused me an issue because I need to serialize then deserialize the POJO, and the deserialization failed because it could not map "cid" json field to "cId" java field.
There are various workarounds (I am using #JsonAlias("cid") on the field to allow the deserialization), but I am puzzled : is this an expected behavior by Jackson ? does it process String fields differently depending on their length ? or is it a java beans convention that I am not aware of ?
Is there a property to set in the objectMapper to "fix" the behavior, without implementing my own com.fasterxml.jackson.databind.PropertyNamingStrategy ?
The problem seems to be caused by how JavaBeans methods get generated when there's a single lowercase character at the beginning of the property name. You might be surprised by the fact that getpId and getcId are indeed correctly named, just as I was.
In short, pId correctly results in the getter getpId rather than the Lombok-generated getPId (the one JavaBeans should have kept, in my opinion).
Now, the interesting part is that Jackson makes cid and pid out of getCId and getPId, respectively, for some reason... while at the same time producing cId and pId for getcId and getpId.
So while getcId and getpId are a quirk of JavaBeans, it seems that Jackson is behaving correctly by default, when the getters are correctly named, i.e., getpId -> "pId" and getcId -> "cId".
Given that Lombok generates getPId and getCId, which lead to the all-lowercase keys in the resulting JSON, deserialization does not work.
If you don't like the getpId/getcId naming, then you may have to write your own getters and force a property name explicitly:
#Builder
#Jacksonized
class ComputationResult {
private final String pId;
private final String cId;
#JsonProperty("pId")
public String getPId() {
return pId;
}
#JsonProperty("cId")
public String getCId() {
return cId;
}
}
For the setters, you already have to rely on #Jacksonized because of final fields anyway, so it doesn't make a difference. Just note that #Getter has to be omitted because it'd result in duplicate properties (with the other kind of naming, that is).

How does Jackson #JsonProperty() work when used to annotate private fields?

Specifically I am wondering how when deserializing an object the deserializer could set a private field? Thinking of an example class like this:
public class MyClass {
#JsonProperty( "My String" );
private String myString;
}
If this is deserialized using objectMapper.readValue(json, MyClass.class); how does the resulting object have this field set if it is marked as private?
Calling Field.setAccessible(true) before reading or writing a value through reflection does the trick here.
For details see the corresponding javadoc: https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/AccessibleObject.html#setAccessible-boolean-
But use with care ;-)
Quite a few frameworks allow access to private fields in this manner by using Field.setAccessible(true). It allows the application to ignore the Java language visibility rules (i.e. private) and read or change the value via an instance of the Reflection API Field class.
A little more can be found in this question:
Java reflection - impact of setAccessible(true)
The short answer is that it can't normally. We use lombok to generate the getter/setter for the variables, but you can of course write your own. Jackson has the same visibility as your main code does, so a private field cannot be mapped without some public getter/setter OR configuring the object mapper like so... objectMapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);.
It wouldn't be able to serialize that either normally. You can use Lombok #Getter and #Setter on the class level so Jackson can work with myString, or put #JsonAutoDetect(fieldVisibility = Visibility.ANY) at the class level like below.
#JsonAutoDetect(fieldVisibility = Visibility.ANY)
public class MyClass {
#JsonProperty( "My String" );
private String myString;
}

How does gson maps JSON keys with the fields in Java classes while deserializing the JSON

I am searching for this for quite some time now but still, it is not clear to me. I have a JSON file which looks like this:
{
"Name" : "Foo Bar",
"Grade" : "Some Grade",
"Org" : "Some Org"
}
For deserializing this JSON (using gson) I have created a Java class called StudentDetails.java which looks like this:
public class StudentDetails
{
public String name;
public String grade;
public String org;
}
Now I have a couple of questions regarding this:
Will gson automatically maps the fields in StudentDetails.java with corresponding keys even if the fields start with lower case and keys start from upper case in the JSON file. I have looked for #SerializedName but my code works without even using it. On the contrary if I am using something like #SerializedName("Name) with name field, it's getting assigned to null after deserialization. I am so confused right now.
Will deserialization work without even getter and setter methods? In jackson you write setter and getter methods.
If above is true, does it work even in the case of private fields?
I'm note sure about this one but i think the case only matters after the first character because you normally don't start the name of field with an upper-case character.
Yes GSON will automatically map the fields.
Yes GSON does not need getter/setter
(https://stackoverflow.com/a/6203975/4622620)
Yes GSON can handle private fields because it uses reflections (https://stackoverflow.com/a/28927525/4622620)

Unable to deserialize alternate name with GSON, AutoValue, and Retrofit 2

I am using retrofit version 2.1.0 to deserialize JSON into pojos. A field in the pojo can be received under different names in the json. To deserialize the field correctly, I used the #serializedName annotation in the following way:
#AutoValue
public abstract class Media implements Parcelable {
#SerializedName(value = "title", alternate = {"name"})
public abstract String title();
// More fields and code
However, for some reason, when the resulting JSON has the field under the key "title", Gson reads it correctly, but when the field is associated with the "name" key, it does not get read.
How can I get GSON to recognize the alternate name during deserialization?
I'm assuming you're using the com.ryanharter.auto.value:auto-value-gson plugin. Support for alternate serialized names was not added until version 0.4.0. Update to com.ryanharter.auto.value:auto-value-gson:0.4.2 and you should then be able to deserialize alternate names.
Seem the problem is related to Parcel.
You might want take a look at this
parceler
#AutoValue
#Parcel
public abstract class Media {
#ParcelProperty("title") public abstract String title();
}

Can we change instance variable name with special character in jaxb

I am using JAXB and jersey for my project. Here is my model class:
#XmlRootElement(name="volume")
#JsonRootName(value="volume")
public class Volume{
#XmlAttribute(name="os-vol:name")
public String name;
}
Desire Output:
{"volume":{"os-vol:name":"vol-1"}}
Is there any way so that i could change the instance variable "name" to "os-vol:name" in both xml and json. I used #JsonProperty,but it worked for JSON only. Any other way so that it can represent both JSON as well as XML simultaneously.
Use the annotation JsonProperty:
#JsonProperty(name="os-vol:name")
public String name;

Categories