Why is Jackson mapping these values twice, in differing case? - java

I'm mapping a Java object to JSON using Jackson, the object is a pretty simple pojo class that looks like this:
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonProperty;
#JsonAutoDetect
public class Area {
#JsonProperty("Id")
public int Id;
#JsonProperty("Name")
public String Name;
public Area() {
Name = "";
}
public int getId() {
return Id;
}
public void setId(int id) {
Id = id;
}
public String getName() {
return Name;
}
public void setName(String Name) {
this.Name = Name;
}
}
The mapping code then looks like this:
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibilityChecker(mapper.getSerializationConfig().getDefaultVisibilityChecker()
.withFieldVisibility(JsonAutoDetect.Visibility.ANY)
.withGetterVisibility(JsonAutoDetect.Visibility.NONE)
.withSetterVisibility(JsonAutoDetect.Visibility.NONE)
.withCreatorVisibility(JsonAutoDetect.Visibility.NONE));
areaJSON = mapper.writeValueAsString(area);
But the value of areaJSON at this point is then as follows:
{"id":0,"name":"","Name":"","Id":0}
Note the multiple values with differing case.
What am I doing wrong?

Jackson thinks that the fields Id and Name are different properties from the ones returned by the getters because the case is different. Using standard JavaBeans naming conventions, Jackson infers the fields corresponding to the getters are named id and name, not Id and Name.
tl;dr case matters.
There are two simple ways to fix this problem:
Remove the #JsonAutoDetect annotation from this class entirely. I'm pretty sure that the default annotation values are taking precedence over the ObjectMapper's configuration. Alternately:
Don't muck with the ObjectMapper at all. Change the #JsonAutoDetect on the class to
#JsonAutoDetect(
fieldVisibility = Visibility.ANY,
getterVisibility = Visibility.NONE,
setterVisibility = Visibility.NONE,
creatorVisibility = Visibility.NONE
)

You need to annotate getId method with #JsonProperty("Id"), otherwise getId will also be added with lowercase id.

I know that it's an old post, but there is a simpler solution:
use only annotation on fields:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Area {
public int id;
public String name;
public Area() {
name = "";
}
public int getId() {
return id;
}
public void setId(int id) {
id = id;
}
public String getName() {
return name;
}
public void setName(String Name) {
this.name = Name;
}
}
You can choose how to serialize the object: using the field or using the properties. If you use the fields, the getter and setter are ignored.
The problem in the code is created by the first letter uppercase : accessing the field, the json property is Id; accessing the getter , getId became id (first letter after get is coded in lower case).

The solution for me was to move annotations to either setters or getters (either one is fine)

Related

Spring Boot parse JSON data to Java Class with different field names

I am new to Spring Boot and I am trying to figure out how to parse json data. I see a lot of tutorials on how to map json string object to an annotated Java class and using and object mapper, like this:
json:
{
"UUID": "xyz",
"name": "some name"
}
public class MyClass{
#JsonProperty
private UUID id;
#JsonProperty
private String name;
#JsonAnyGetter
public UUID getId() {
return this.id;
}
#JsonAnySetter
public void setId(UUID id) {
this.id = id;
}
#JsonAnyGetter
public String getName() {
return this.name;
}
#JsonAnySetter
public void setName(String name) {
this.name = name;
}
}
ObjectMapper objectMapper = new ObjectMapper();
MyClass customer = objectMapper.readValue(jsonString, MyClass.class);
The problem is that the system I am getting the json string from does not match the class naming conventions we use (and I cannot change either one). So, instead of having the example json string above, it might look like this:
{
"randomdstring-fieldId": "xyz",
"anotherrandomstring-name": "some name"
}
This use case only has two fields, but my use case has a larger payload. Is there a way to either map the field names from the json object to the field names in the Java class or is there a way to just parse the json string as a key value pair (so that I can just manually add the fields to my Java object)?
In Jackson with #JsonProperty you can customize the field name with it's annotation parameter value
Therefore, you just have to annotate the entity fields with the #JsonProperty annotation and provide a custom JSON property name, like this:
public class MyClass{
#JsonProperty("original_field_name_in_json")
private UUID id;
...
The #JsonProperty will do it for you:
#JsonProperty("name_in_json")
private Long value;

Annotations when fields/columns not match

Suppose you have a class:
#Entity(name = "Person")
public class Person{
#Column(name = "iAge")
private int _age;
}
What I am doing to expose access methods is:
#JsonProperty("age")
public int getAge() {
return _age;
}
#JsonProperty("age")
public void setAge(int _age) {
this._age = _age;
}
Is there a better way without duplicating annotations?
Note that the field name (_age) is different from the column of the table (iAge).
Actually it's cannot be done because #JsonProperty and #Coulmn is a different frameworks annotations, the best way I can think you can do it in more pretty way is like that :
#Entity(name = "Person")
public class Person{
#Column(name = "iAge")
#JsonProperty("age")
private int _age;
}
And make your ObjectMapper look at your fields
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
If you have the names as in this particular example - then you may omit #JsonProperty as the name of the property will be automatically inherited from getAge() method name.

XmlTransient annotation on getter only?

I am trying to unmarshal an XML into an object that I expect should have a certain field. However, I do not want to marshal that object into an XML that contains it. What I like would be similar to this:
#XmlRootElement(name = "User")
public class User {
private String name;
#XmlTransient
public String getName() {
return this.name
}
#XmlElement(name = "Name")
public void setName(String name) {
this.name = name
}
}
However, this would not work due to the conflicting annotations, as I can't use any other XML annotations with #XmlTransient. I have also tried to add the #XmlTransient annotation on the field itself instead of the getter and have set this option:
XmlAccessorType(XmlAccessType.FIELD)
In addition, I kept the #XmlElement annotation on the setter, and that did absolutely nothing in terms of excluding the field from being marshalled.
I would like to keep the #XmlElement annotation, since I like being able to translate a field with a different name (here it is just a capitalization difference) into whichever field I want.
I also cannot delete the getter, as I do use it in the application.
Given that, I don't know what my options are at this point, other than writing an adapter (which I could do, but if there is another solution, I'd rather not use a custom adapter because of this one field). Any help would be greatly appreciated.
I think your problem lies in the idea itself: #XmlTransient tells the marshaller to completely ignore that field/property when doing its job, so I'd guess it's not what you're looking for, as you wouldn't want to (and couldn't anyway) set a custom name for the marshalled element if you wanted to omit it in the first place.
Another point is that with JAXB, public getters/setters are paired with their respective counterparts, so the annotations applied to both are "merged" when read (hence why you can't use #XmlTransient in the getter and #XmlElement in the setter at the same time), and thus their positions are also interchangeable.
Also, just for clarity, #XmlAccessorType only interferes in the default handling of public members. If the field or method in question is not public, it won't affect how it will be handled by default.
Now for the solutions:
If you want to omit it all:
With private field and public getter/setter, just use #XmlTransient once in the getter or setter and nothing else.
#XmlRootElement(name = "User")
public class User {
private String name;
#XmlTransient
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
If both are public, use #XmlTransient once in the field and once again in either getter or setter.
#XmlRootElement(name = "User")
public class User {
#XmlTransient
public String name;
#XmlTransient
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
If instead you want to keep it with a custom name:
If the field is private, use only #XmlElement once in the getter or setter.
#XmlRootElement(name = "User")
public class User {
private String name;
#XmlElement(name = "Name")
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
If both field and accessors are public (and there's no #XmlAccessorType or it's set to XmlAccessType.PUBLIC_MEMBER), then you'll have to use #XmlTransient in either field or getter/setter and #XmlElement in the other (they'll be interchangeable if all the methods do is just reading/writing the value, as in this case).
#XmlRootElement(name = "User")
public class User {
#XmlTransient
public String name;
#XmlElement(name = "Name")
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}

AWS DynamoDB - save a JSON Array to a table (Java/Android)

How do you save a JSON Array as an item attribute? AWS documentation is the absolute worst thing ever - it contradicts itself, a lot of things are either redundant or only partially explained, some things aren't explained at all - I don't know how anyone manages to use it.
Anyway, suppose I have a table called Paths, and a path has a name, an ID, and a list of LatLngs (formatted as a JSON Array)
In the class definition for this table, I have
#DynamoDBTable(tableName = "Paths")
public class Path {
private String id;
private String name;
private JSONArray outlineJSON;
with getters and setters like
#DynamoDBRangeKey(attributeName = "name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
which works fine for strings, booleans and numbers, and the object saves successfully to the table.
AWS documentation mentions JSON several times, and says it can handle lists, but it doesn't explain how to use lists or give any examples.
I used #DynamoDBHashKey for the id, #DynamoDBRangeKey for name, and #DynamoDBAttribute for other strings, numbers or booleans, and I tried it here
#DynamoDBAttribute(attributeName = "outline")
private JSONArray getOutlineJSON() {
return outlineJSON;
}
private void setOutlineJSON(JSONArray outlineJSON) {
this.outlineJSON = outlineJSON;
}
It successfully saved the object but without the array.
How do I save the array? I can't find an explanation anywhere. I think #DynamoDBDocument might have something to do with it, but all the documentation on the subject gives unrelated examples, and I can't find any using a list like my in situation.
EDIT: For now, I have a working solution - I can easily convert my lists to JSONArrays and then convert those to Strings, and vice-versa.
You can define your class to be something like
#DynamoDBTable(tableName = "Paths")
public class Path {
private String id;
private String name;
private LatLang latLangs;
#DynamoDBHashKey(attributeName="id")
public String getId() { return id;}
public void setId(String id) {this.id = id;}
#DynamoDBRangeKey(attributeName = "name")
public String getName() { return name; }
public void setName(String name) { this.name = name; }
#DynamoDBDocument
public static class LatLang{
public String lat;
public String lang;
}
}

Parsing two isomorphous XML schemas into one class structure using JAXB

Consider two isomorphous XML schemas. By isomorphism here I mean that these two schemas have identical structures except attributes and tags names. More specifically I have live example when was schema, say A, and its copy B, where all tags and attribute names were translated from English into national lamguage equivalents.
For example, as input we can have two different variants of one object:
<tag_1_v1>
<tag_2_v1 id="blabla" name="xxxxx">
Some value1
</tag_2_v1>
<tag_3_v1 id="alalala" name="yyyyy">
Some value2
</tag_3_v1>
</tag_1_v1>
and
<tag_1_v2>
<tag_2_v2 special_id_2="blabla" name="xxxxx">
Some value1
</tag_2_v2>
<tag_3_v2 id="alalala" special_name_2="yyyyy">
Some value2
</tag_3_v2>
</tag_1_v2>
The problem is to map these two schemas on single class structure, say
class Tag1 {
Tag2 tag2;
Tag3 tag3;
}
class Tag2 {
String id;
String name;
String value;
}
class Tag3 {
String id;
String name;
String value;
}
There are various ideas how to workaround this issue, but all of them aren't so convinient, as any possibility to use single JAXB annotation scheme on same class structure. They are:
create two different class-sets and then copy values from objects of
one schema into another;
create own SAX parser implementation and "translate" inside it tag and attribute names into appropriate ones;
use own preprocessor of XML and use string replacement (will not work if id and attributes name aren't identical within all schema).
Since each <tag_i> can have different attributes, a clean solution would be to use inheritance:
Create an abstract class Tag1 that is inherited by Tag1V1 and Tag1V2. Factor all the common code into Tag1.
The same would go Tag2 and Tag3.
To get you started, here would be an implementation of Tag2:
#XmlRootElement
#XmlSeeAlso({Tag2V1.class, Tag2V2.class})
abstract class Tag2 {
private String name;
private String content;
#XmlAttribute(name = "name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#XmlValue
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
#XmlRootElement(name = "tag_2_v1")
class Tag2V1 extends Tag2 {
private String id;
#XmlAttribute(name = "id")
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
#XmlRootElement(name = "tag_2_v2")
class Tag2V2 extends Tag2 {
private String specialId2;
#XmlAttribute(name = "special_id_2")
public String getSpecialId2() {
return specialId2;
}
public void setSpecialId2(String specialId2) {
this.specialId2 = specialId2;
}
}

Categories