Bhavesh Shah
Bhavesh Shah

Reputation: 3389

How to map same @JsonAlias to different object fields using Jackson ObjectMapper

In my DTO class, there could be fields which might have same @JsonAlias values, but this seems not working with Jackson ObjectMapper.

It seems that ObjectMapper only works for the first occurrence of @JsonAlias and it don't work for the rest @JsonAlias which has same value. I have tried to create an example below for the reference and in that example, Person class has the two fields with name @JsonAlias value.

Code snippet:

import com.fasterxml.jackson.annotation.JsonAlias;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.HashMap;
import java.util.Map;

public class Test {

  public static void main(String[] args) {

    Map<String, String> values = new HashMap<>();
    values.put("name", "TEST NAME");

    Person person = new ObjectMapper().convertValue(values, Person.class);
    System.out.println("name1: " + person.getName1());
    System.out.println("name2: " + person.getName2());
  }

  static class Person {
    @JsonAlias("name")
    String name1;

    @JsonAlias("name")
    String name2;

    public String getName1() {
      return name1;
    }

    public void setName1(String name1) {
      this.name1 = name1;
    }

    public String getName2() {
      return name2;
    }

    public void setName2(String name2) {
      this.name2 = name2;
    }
  }
}

Output:

name1: TEST NAME
name2: null

In the above output. I was expecting the "TEST NAME" for the name2 variable in Person class.

Is there any configuration in Jackson ObjectMapper which will help me to achieve this?

Jackson version - jackson-databind-2.11.4.jar

Upvotes: 0

Views: 3446

Answers (1)

Eugene
Eugene

Reputation: 6015

Solution 1, @JsonGetter @JsonSetter point out to the custom getter\setter
We can use @JsonGetter("name") and @JsonSetter("name") annotations to map one JSON property value to the setter which has handling two properties

public class Person {
    @JsonIgnore
    private String name1;

    @JsonIgnore
    private String name2;

    @JsonSetter("name")
    public void setName(String name) {
        this.name1 = name;
        this.name2 = name;
    }

    @JsonGetter("name")
    public String getName1() {
        return name1;
    }

    public void setName1(String name1) {
        this.name1 = name1;
    }

    public String getName2() {
        return name2;
    }

    public void setName2(String name2) {
      this.name2 = name2;
    }
}

Solution 2, @JsonAlias with the specific setter
We can ignore one field, the second mark with an alias, and add a custom setName setter that maps one JSON value to several fields.

public class Person {
    @JsonAlias("name")
    private String name1;

    @JsonIgnore
    private String name2;

    public String getName1() {
        return name1;
    }

    public void setName1(String name1) {
        this.name1 = name1;
    }

    public String getName2() {
        return name2;
    }

    public void setName2(String name1) {
        this.name1 = name1;
    }

    public void setName(String name) {
        this.name1 = name;
        this.name2 = name;
    }
}

Or just add a custom setName setter to your original code

public class Person {
    @JsonAlias("name")
    String name1;

    @JsonAlias("name")
    String name2;

    public String getName1() {
        return name1;
    }

    public void setName1(String name1) {
        this.name1 = name1;
    }

    public String getName2() {
        return name2;
    }

    public void setName2(String name1) {
        this.name1 = name1;
    }

    public void setName(String name) {
        this.name1 = name;
        this.name2 = name;
    }
}

Solution 3, @JsonProperty with the specific setter
The same like Solution 2, but instead use @JsonProperty("name")

public class Person {
    @JsonProperty("name")
    private String name1;

    @JsonIgnore
    private String name2;

    public String getName1() {
        return name1;
    }

    @JsonIgnore
    public void setName1(String name1) {
        this.name1 = name1;
    }

    public String getName2() {
        return name2;
    }

    public void setName2(String name1) {
        this.name1 = name1;
    }

    public void setName(String name) {
        this.name1 = name;
        this.name2 = name;
    }
}

UPDATE:
In case you do not have the ability to modify DTO object, custom deserializer resolve the problem.

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;

import java.io.IOException;

public class PersonDeserializer extends StdDeserializer<Person> {

    public PersonDeserializer() {
        this(null);
    }

    public PersonDeserializer(Class<?> vc) {
        super(vc);
    }

    @Override
    public Person deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
        Person person = new Person();

        JsonNode node = jp.getCodec().readTree(jp);
        String name =  node.get("name").asText();
        person.setName1(name);
        person.setName2(name);

        return person;
    }
}

Apply deserializer to the DTO object:

@JsonDeserialize(using = PersonDeserializer.class)
public class Person {
    private String name1;
    private String name2;

    //getters and setters
}

OR register custom deserializer on ObjectMapper

        ObjectMapper mapper = new ObjectMapper();
        SimpleModule module = new SimpleModule();
        module.addDeserializer(Person.class, new PersonDeserializer());
        mapper.registerModule(module);

Upvotes: 2

Related Questions