Jackson conditional @JsonUnwrapped

Can I use @JsonUnwrapped conditionally? I don't want to use it during serialization but would like to use it while deserializing the object.

One way to do it is create two different classes or create a subclass overriding just that property which needs to behave different while serializing and deserializing. This doesn't sound right. Any other alternatives or Jackson way of tackling the problem?


Solution 1:

There is a simpler way to achieve the same result that is based on annotations only:

@JsonUnwrapped(prefix = "name_")
@JsonProperty(access = READ_ONLY)
private Name nameObject;

// method will deserialize original JSON and set it to unwrapped field
@JsonSetter
public void setName(Name name) {
  this.nameObject = name;
}

// simple getter
@JsonIgnore
public Name getName() {
  return this.nameObject;
}

Solution 2:

You can use MixIn feature. Using this feature POJO class is decoupled from Jackson annotations. You can add necessary annotation in runtime using MixIn. See below example:

import com.fasterxml.jackson.annotation.JsonUnwrapped;
import com.fasterxml.jackson.databind.ObjectMapper;

public class JacksonTest {

    private static final String UNWRAPPED_JSON = "{\n" +
            "  \"age\" : 13,\n" +
            "  \"first\" : \"Huckleberry\",\n" +
            "  \"last\" : \"Finn\"\n" +
            "}";

    @Test
    public void test() throws IOException {
        System.out.println("### Serialize without unwrapped annotation ###");
        ObjectMapper serializer = new ObjectMapper();
        System.out.println(serializer.writerWithDefaultPrettyPrinter().writeValueAsString(createParent()));

        System.out.println("### Deserialize with unwrapped annotation ###");
        ObjectMapper deserializer = new ObjectMapper();
        deserializer.addMixInAnnotations(Parent.class, ParentMixIn.class);
        System.out.println(deserializer.readValue(UNWRAPPED_JSON, Parent.class));
    }

    private Parent createParent() {
        Name name = new Name();
        name.first = "Tom";
        name.last = "Sawyer";

        Parent parent = new Parent();
        parent.setAge(12);
        parent.setName(name);

        return parent;
    }
}

class Parent {

    private int age;
    private Name name;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Name getName() {
        return name;
    }

    public void setName(Name name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Parent{" +
                "age=" + age +
                ", name=" + name +
                '}';
    }
}

interface ParentMixIn {

    @JsonUnwrapped
    Name getName();
}

class Name {

    public String first, last;

    @Override
    public String toString() {
        return "Name{" +
                "first='" + first + '\'' +
                ", last='" + last + '\'' +
                '}';
    }
}

Above program prints:

### Serialize without unwrapped annotation ###
{
  "age" : 12,
  "name" : {
    "first" : "Tom",
    "last" : "Sawyer"
  }
}

### Deserialize with unwrapped annotation ###
Parent{age=13, name=Name{first='Huckleberry', last='Finn'}}