Skip to content
Advertisement

JSON data mapping in Java with Jackson

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).

Advertisement