Unexpected duplicate key error using #JsonTypeInfo property - json

I have a simple hierarchy of data objects, which have to be converted to JSON format. Like this:
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "documentType")
#JsonSubTypes({#Type(TranscriptionDocument.class), #Type(ArchiveDocument.class)})
public class Document{
private String documentType;
//other fields, getters/setters
}
#JsonTypeName("ARCHIVE")
public class ArchiveDocument extends Document { ... }
#JsonTypeName("TRANSCRIPTIONS")
public class TranscriptionDocument extends Document { ... }
Upon JSON parsing I encounter errors like this one:
Unexpected duplicate key:documentType at position 339. , because in the generated JSON there are actually two documentType fields.
What should be changed to make JsonTypeName value appear in documentType field, without an error (eg replacing the other value)?
Jackson version is 2.2

Your code doesn't show it, but I bet you have a getter in your Document class for the documentType property. You should annotate this getter with #JsonIgnore like so:
#JsonIgnore
public String getDocumentType() {
return documentType;
}
There is an implicit documentType property associated with each subclass, so having the same property in the parent class causes it to be serialized twice.
Another option would be to remove the getter altogether, but I assume you might need it for some business logic, so the #JsonIgnore annotation might be the best option.

Related

Serialize List of subclasses not working in JsonUtility in Unity

When using JsonUtility to serialize in Unity, List of a class will be serialized as empty string if it's filled with subclasses of ExampleObjtype.
[Serializable]
public class SerializableGameEntityDebugSubclass : SerializableGameEntityDebug {
public SerializableGameEntityDebugSubclass() : base() {}
}
[Serializable]
public abstract class SerializableGameEntityDebug {
public string uuid = null;
public SerializableGameEntityDebug() {
this.uuid = "debuggin";
}
}
public class GameSaveData
{
public List<GameEntity.SerializableGameEntityDebugSubclass> serializableGameEntitiesDebug1 = new List<GameEntity.SerializableGameEntityDebugSubclass>{ new SerializableGameEntityDebugSubclass() };
public List<GameEntity.SerializableGameEntityDebug> serializableGameEntitiesDebug2 = new List<GameEntity.SerializableGameEntityDebug>{ new SerializableGameEntityDebugSubclass() };
}
serializableGameEntitiesDebug1 DOES get subclassed and serializableGameEntitiesDebug1 does NOT get subclassed. I find this very odd because even if I print out individually the serialized elements of the list, it works correctly in both cases.
There are two separate issues at play.
It seems JsonUtility won't serialize List of any abstract class no matter what. So the thing the list contains must not be an abstract class
When I change the abstract class to a regular class, it will serialize it, but it will only contain fields in the base class rather than child classes.
Therefore it seems the only workaround is to have many lists to serialize (one for each child class)
Update: A slightly more elegant solution was to switch from using JsonUtility to Json.net JsonConverter. This caused serialization to work perfectly, but not yet deserialization. I still had to write a converter class so the deserializer knows which class to instantiate. I followed this answer and it worked. Last but not least it seems that each serializable class needs to have a default empty constructor for the deserializer to call when trying to instantiate it before hydrating it, or else it might try to call other constructors with null args

Json Ignore Property on JsonSubTypes

I have a Scala case class that represents my JSON as below:
class MyJSON(x: Int, typeA: TypeA, typeB: TypeB)
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
#JsonSubTypes(Array(
new Type(value = classOf[ClassA], name = "ClassA"),
new Type(value = classOf[ClassB], name = "ClassB")))
trait TypeA {
...
...
}
In my ClassA, I have certain fields that are deserialized from the JSON. But I also want that if there are certain fields that are not part of my Class objects, I want them to be ignored. What I did was I used the #JsonIgnoreProperties(ignoreUnknown=true) annotation on the MyJSON class as below:
#JsonIgnoreProperties(ignoreUnknown=true)
class MyJSON(x: Int, typeA: TypeA, typeB: TypeB)
It failed when my input JSON had some unknown fields. But when I moved this annotation to one of my Class (say ClassA) in my case, it was ignored. The problem is that I do not want to add this ignore properties annotation to all my classes, but rather I add it just to the top and want that propagated to all the types.
Try this
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
I found a much elegant solution to the problem. I used Jackson MixIn mechanism to actually add the additional annotations to my target types during runtime. Here is what I did:
#JsonIgnoreProperties({ Array("field1", "field2") })
abstract class MixInAnnotations{}
In my JSONMarshaller class where I create the ObjectMapper, I do the following:
val mapper = new ObjectMapper()
mapper.registerModule(new MyApplicationModule)
The MyApplicationModule now looks like:
public class MyApplicationModule extends com.fasterxml.jackson.databind.module.SimpleModule {
public MyApplicationModule() {
super(MyApplicationModule.class.getSimpleName());
// Add the corresponding MixIn Annotations to ignore the JSON properties
setMixInAnnotation(MyTargetClass1.class, MixInAnnotations.class);
setMixInAnnotation(MyTargetTrait.class, MixInAnnotations.class);
}
}
I call the setter methods to add the MixInAnnotations to my target classes. The target classes here could also be a trait that has the JsonSubTypes Annotation. So effectively, I'm not polluting all my types with the #JsonIgnoreProperties annotation.

Jackson JsonTypeInfo.As.EXTERNAL_PROPERTY doesn't work as expected

I am using Jackson to parse JSON that I have no control over. The JSON looks like this:
{
"status":"0"
"type":"type1"
"info": {
// additional fields
}
}
My class looks like this
public class Response {
private String status;
private String type;
private Info info
}
The subclass of Info that I use depends on the type property, so my mapping for info is
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "type")
#JsonSubTypes(value = {
#JsonSubTypes.Type(value = Type1Info.class, name = "type1"),
#JsonSubTypes.Type(value = Type2Info.class, name = "type2") })
public abstract class Info {
// some fields
}
As far as I can tell this is the correct way to use type info when the distinguishing element is at the same level as the element that has to be casted. But this doesn't work, I always get the same error:
com.fasterxml.jackson.databind.JsonMappingException: Unexpected token
(END_OBJECT), expected FIELD_NAME: missing property 'type' that is to
contain type id
If I change EXTERNAL_PROPERTY to PROPERTY I still get the same error. Is my understanding of EXTERNAL_PROPERTY wrong?
From Javadoc:
Inclusion mechanism similar to PROPERTY, except that property is
included one-level higher in hierarchy, i.e. as sibling property at
same level as JSON Object to type. Note that this choice can only be
used for properties, not for types (classes). Trying to use it for
classes will result in inclusion strategy of basic PROPERTY instead.
Noticed that can only be used for properties is bolded. Source: JsonTypeInfo.As.EXTERNAL_PROPERTY.
So, you have to move all annotation from Info class to property info or setInfo method in Response class.
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "type")
#JsonSubTypes(value = { #JsonSubTypes.Type(value = Type1Info.class, name = "type1"),
#JsonSubTypes.Type(value = Type2Info.class, name = "type2") })
public void setInfo(Info info) {
this.info = info;
}
For me, you should also remove type property from Response class. It will be generated dynamically during serialization process. In deserialization you do not need it because Jackson cares about types. Your class could look like this:
class Response {
private String status;
private Info info;
//getters, setters
}
See also this question: JSON nest class data binding.
For all those who use AutoValue or any other source code generator: #JsonTypeInfo and #JsonSubTypes annotations does not work when you annotate your abstract methods (which in generated code returns the actual property).
If you do it this way, you will get following exception:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of [xxx] (no Creators, like default construct, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
Also it not works when you annotate parameter in your method annotated as #JsonCreator. If you do it this way you would get this weird exception: com.fasterxml.jackson.databind.exc.MismatchedInputException: Unexpected token (START_OBJECT), expected START_ARRAY: need JSON Array to contain As.WRAPPER_ARRAY type information for class [xxx]
So to deal with it just dont use source code generator for this class, just map the Response object using common jackson annotations: #JsonCreator, #JsonProperty and #JsonTypeInfo, #JsonSubTypes
tested with jackson 2.10.5

Jackson deserialization when POJO does not match JSON structure

I have some json :
{
key: "CORE-19",
fields: { summary: "iblah" }
}
I want to pack it into a POJO that looks more like:
#JsonIgnoreProperties(ignoreUnknown=true)
public class JiraIssue
{
private String mKey;
private String mSummary;
public String getKey(){ return(mKey);}
public void setKey(String inKey){mKey = inKey;}
public String getSummary(){return(mSummary);}
public void setSummary(String summary){ mSummary = summary; }
}
So basically I don't want to create a 'Fields' object as it is a bit superfluous for my needs. However I really can't see any way in Jackson to tell it that the 'summary' property actually comes from the 'fields' property. Is this possible?
Serialization of this class is not a concern, it will only ever be used for Deserialization. I have no control over the JSON format as it is coming from an external source (and the above is just a snippet). Also I'm actually using Jackson with Jersey.
Thanks!
There is actually an open issue for this kind of structural change. There is no way as of now to do that easily with annotation only without modifying your class. What you could do instead is handle the "fields" property as a "false" property, by adding the following method:
public void setFields(Map<String, String> fields) {
setSummary(fields.get("summary"));
}
This way you "unwrap" the property yourself.
Try:
#JsonProperty("fields.summary")
private String mSummary;

Jackson passing null value to constructor

Considering a Java class constructor that take two parameters but allows the second to be null.
public class Category {
String name;
#JsonIgnore Category parent;
Category(String name,Category parent){this.name = name;this.parent=parent;}
}
I skipped the serialization of parent with #JsonIgnore annotation because I don't need it. Now Jackson is not capable to deserialize it because it don't find the parent property in the resulting Jason.
Is there any another solution but to define a constructor taking only the name parameter?
It is ok to have named constructor parameters that are missing -- you will simply get null instead of value. So you could just have:
#JsonCreator
public Category(#JsonProperty("name") String name, #JsonProperty("whatever") Category parent) { ... }
and whatever is found is passed. No exception will be thrown; Jackson never requires a property to exist. It just complains about things it does not recognize (unless configured not to).