I got stuck in mapping a json into my data structures “the easy way” using just Jackson decorators and I was wondering if there is a way to do this …
The json that I try to read has the following structure:
{ "animals": [ {"data_info":{"ns":"dog"}, "sound":"bowwow", "bites":True}, {"data_info":{"ns":"dog"}, "sound":"woofWoof", "bites":False}, {"data_info":{"ns":"cat"}, "sound":"meeeOwww", "age":5} ], "data_info":{"ns":"animal"} }
So basically every data entity has a “data_info” object (mapped in my code from below to DataTypeInfo) that has a property “ns” object (mapped in my code TypeInfo) which contains the object type. So this means that the discriminator for object types is always under data_info.ns
Here are my data entities:
public class Animals extends DataTypeInfo { @JsonProperty("animals") List<Mamals> animals; } @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "data_info.ns") @JsonSubTypes({ @JsonSubTypes.Type(value = Cat.class, name = "cat"), @JsonSubTypes.Type(value = Dog.class, name = "dog"), }) public abstract class Mamals extends DataTypeInfo { } public class Cat extends Mammals { @JsonProperty("sound") private String sound; @JsonProperty("age") private in age; } public class Dog extends Mammals { @JsonProperty("sound") private String sound; @JsonProperty("bites") boolean bites } public class DataTypeInfo { @JsonProperty("data_info") TypeInfo typeInfo; } public class TypeInfo { @JsonProperty("ns") String nameSpace; }
The error in my code is in the discriminator from the Mammals class: property = “data_info.ns” since this is intended to work with properties but I try to use a sub property … Is there a way to correctly declare the discriminator of the Mammal abstract class so that the correct Dog or Cat objects are instantiated ?
Advertisement
Answer
The solution that I ended up with was to use a custom builder (JsonCreator) in the abstract class (for the example from above Mammals).
My updated Mammals class looks like this:
@JsonCreator public static Mammals create(Map<String,Object> jsonMap) throws JsonProcessingException { Mammals result; Map type_info = (Map<String,String>) jsonMap.get("data_info"); String type = (String) type_info.get("ns"); ObjectMapper mapper = new ObjectMapper(); String json = mapper.writeValueAsString(jsonMap); if (type.equals("cat")) { result = mapper.readValue(json, Cat.class); } else if (type.equals("dog")) { result = mapper.readValue(json, Dog.class); } else { throw new RuntimeException("Unknown entity type"); } return result; }
Since my root class (Animals) contains a list of Mammals, for every element of this list this creator is executed to build the proper instance of Mammals (Cat or Dog in my example).