Search code examples
javajsonjacksonjson-deserializationjackson2

How Jackson handles mapping conflict?


I have the following class with a property "key" that maps to 2 different JSON fields

public class A {

    @JsonAlias("Key")
    private String key;

    @JsonProperty("NewKey")
    private void unpackNewKey(Map<String, String> NewKey) {
        key = NewKey.get("value");
    }
}

Here is the JSON to deserialize.

{
    "NewKey": {
        "value": "newkey",
    },
    "Key": "key"
}

If I deserialize the above json to A.class

ObjectMapper mapper = new ObjectMapper();
A a = mapper.readValue(json, A.class)

What would be the value of the a.key? will it be newkey or key? Trying to understand how jackson handles conflict. Can I specify the order? for example, if I want the key to always map to NewKey if both Key and NewKey exist in the json, what should I do?


Solution

  • In your example, where you use @JsonAlias("Key") and @JsonProperty("NewKey") order depends from an order of key-value pairs in JSON payload. If you want to always deserialise with NewKey key precedence, you need to implement this in custom JSON Deserialiser or in constructor. Simple example you can find below:

    import com.fasterxml.jackson.annotation.JsonCreator;
    import com.fasterxml.jackson.databind.ObjectMapper;
    
    import java.util.Collections;
    import java.util.Map;
    import java.util.Objects;
    
    public class JsonApp {
    
        public static void main(String[] args) throws Exception {
            ObjectMapper mapper = new ObjectMapper();
            System.out.println(mapper.readValue("{}", A.class));
            System.out.println(mapper.readValue("{\"Key\": \"key\"}", A.class));
            System.out.println(mapper.readValue("{\"NewKey\": {\"value\": \"newkey\"}}", A.class));
            System.out.println(mapper.readValue("{\"Key\": \"key\", \"NewKey\": {\"value\": \"newkey\"}}", A.class));
            System.out.println(mapper.readValue("{\"NewKey\": {\"value\": \"newkey\"}, \"Key\": \"key\"}", A.class));
        }
    }
    
    class A {
    
        private String key;
    
        @JsonCreator
        public A(Map<String, Object> json) {
            final String key = Objects.toString(json.get("Key"), null);
            final Map newKey = (Map) json.getOrDefault("NewKey", Collections.emptyMap());
            this.key = Objects.toString(newKey.get("value"), key);
        }
    
        public String getKey() {
            return key;
        }
    
        public void setKey(String key) {
            this.key = key;
        }
    
        @Override
        public String toString() {
            return key;
        }
    }
    

    Above code prints:

    null
    key
    newkey
    newkey
    newkey