Jackson deserialize based on property name Jackson deserialize based on property name json json

Jackson deserialize based on property name


Jackson doesn't offer an out of the box solution for that, but it doesn't mean that you are out of luck.


Assuming that your classes implement a common interface or extend a common class, as shown below:

public interface Animal {}
public class Dog implements Animal {   private String bark;      // Default constructor, getters and setters}
public class Cat implements Animal {   private String meow;      // Default constructor, getters and setters}

You can create a custom deserializer based on the property name. It allows you to define a unique property that will be used to look up the class to perform the deserialization to:

public class PropertyBasedDeserializer<T> extends StdDeserializer<T> {    private Map<String, Class<? extends T>> deserializationClasses;    public PropertyBasedDeserializer(Class<T> baseClass) {        super(baseClass);        deserializationClasses = new HashMap<String, Class<? extends T>>();    }    public void register(String property, Class<? extends T> deserializationClass) {        deserializationClasses.put(property, deserializationClass);    }    @Override    public T deserialize(JsonParser p, DeserializationContext ctxt)            throws IOException, JsonProcessingException {        ObjectMapper mapper = (ObjectMapper) p.getCodec();        JsonNode tree = mapper.readTree(p);                Class<? extends T> deserializationClass = findDeserializationClass(tree);        if (deserializationClass == null) {            throw JsonMappingException.from(ctxt,                "No registered unique properties found for polymorphic deserialization");        }        return mapper.treeToValue(tree, deserializationClass);    }        private Class<? extends T> findDeserializationClass(JsonNode tree) {                Iterator<Entry<String, JsonNode>> fields = tree.fields();        Class<? extends T> deserializationClass = null;                while (fields.hasNext()) {            Entry<String, JsonNode> field = fields.next();            String property = field.getKey();            if (deserializationClasses.containsKey(property)) {                deserializationClass = deserializationClasses.get(property);                break;              }        }                return deserializationClass;    }}

Then instantiate and configure the deserializer:

PropertyBasedDeserializer<Animal> deserializer =         new PropertyBasedDeserializer<>(Animal.class);deserializer.register("bark", Dog.class); // If "bark" is present, then it's a Dogdeserializer.register("meow", Cat.class); // If "meow" is present, then it's a Cat

Add it to a module:

SimpleModule module = new SimpleModule("custom-deserializers", Version.unknownVersion());module.addDeserializer(Animal.class, deserializer);

Register the module and perform the deserialization as usual:

ObjectMapper mapper = new ObjectMapper();mapper.registerModule(module);String json = "[{\"bark\":\"bowwow\"}, {\"bark\":\"woofWoof\"}, {\"meow\":\"meeeOwww\"}]";List<Animal> animals = mapper.readValue(json, new TypeReference<List<Animal>>() { });


With fasterxml jackson, you can do this:

abstract class FooOrBar {    companion object {        @JvmStatic        @JsonCreator        private fun creator(json: Map<String, String>): FooOrBar? {            return when {                json.containsKey("foo") -> Foo(json["foo"] as String)                json.containsKey("bar") -> Foo(json["bar"] as String)                else -> null            }        }    }}class Foo(val foo: String) : FooOrBar() // even can use map delegate if you know what it isclass Bar(val bar: String) : FooOrBar()

It is Kotlin, but you will get the idea.

Note the@JsonCreator is used. the annotated creator function has single argument (which is one kind of the two signatures required by JsonCreator), JSON is deserialized as a Map instance and passed to the creator. From here, you can create your class instance.

----------------UPDATE-------------------------

You can also use JsonNode for the creator function for nested and complex JSON.

private fun creator(json: JsonNode): FooOrBar?


You'll have to tell jackson what class you expect:

Foo readValue = mapper.readValue(json, Foo.class);

Bar readValue = mapper.readValue(json, Bar.class);

Otherwise it may be worth using XML in this case if you strong types are necessary for your design.