62

Say I have the following class:

public class Parent {
  public int age;
  @JsonUnwrapped
  public Name name;
}

Producing JSON:

{
  "age" : 18,
  "first" : "Joey",
  "last" : "Sixpack"
}

How do I deserialize this back into the Parent class? I could use @JsonCreator:

@JsonCreator
public Parent(Map<String,String> jsonMap) {
  age = jsonMap.get("age");
  name = new Name(jsonMap.get("first"), jsonMap.get("last"));
}

But this also effectively adds @JsonIgnoreProperties(ignoreUnknown=true) to the Parent class, as all properties map to here. So if you wanted unknown JSON fields to throw an exception, you'd have to do that yourself. In addition, if the map values could be something other than Strings, you'd have to do some manual type checking and conversion. Is there a way for Jackson to handle this case automatically?

Edit: I might be crazy, but this actually appears to work despite never being explicitly mentioned in the documentation: JsonUnwrapped. I was pretty sure it didn't work for me previously. Still, the proposed @JsonCreator approach might be preferred when custom logic is required to deserialize unwrapped polymorphic types.

M. Justin
  • 14,487
  • 7
  • 91
  • 130
Shaun
  • 2,490
  • 6
  • 30
  • 39
  • 6
    Are you certain `@JsonUnwrapped` is working for deserialization? I just tried it and am getting `Could not read JSON: Unrecognized field...` errors when I try to deserialize the flattened JSON. – E-Riz Apr 23 '14 at 18:47

5 Answers5

39

You can use @JsonCreator with @JsonProperty for each field:

@JsonCreator
public Parent(@JsonProperty("age") Integer age, @JsonProperty("firstName") String firstName,
        @JsonProperty("lastName") String lastName) {
    this.age = age;
    this.name = new Name(firstName, lastName);
}

Jackson does type checking and unknown field checking for you in this case.

hoaz
  • 9,883
  • 4
  • 42
  • 53
  • 2
    Good point. This does answer the question of how to preserve jackson type/field checking in JsonCreators in general, which might be the only useful question remaining. =) – Shaun May 15 '13 at 16:37
16

It does work for deserialization as well, although it's not mentioned in the docs explicitly, like you said. See the unit test for deserialization of @JsonUnwrapped here for confirmation - https://github.com/FasterXML/jackson-databind/blob/d2c083a6220f2875c97c29f4823d9818972511dc/src/test/java/com/fasterxml/jackson/databind/struct/TestUnwrapped.java#L138

spinlok
  • 3,561
  • 18
  • 27
5

@JsonUnwrapped works for both serialization and deserialization, you shouldn't need to take any additional steps.

mech
  • 2,775
  • 5
  • 30
  • 38
Artem Areshko
  • 159
  • 2
  • 6
4

No changes are needed to your source object using @JsonUnwrapped to deserialize JSON back into the Parent class.

Despite not being mentioned in the Javadocs prior to Jackson 2.13 (per jackson-annotations#184), the @JsonUnwrapped annotation applies to deserialization as well as serialization, so no additional configuration is needed to support deserialization of a field using the annotation.

The Jackson 2.13 Javadocs for @JsonUnwrapped clarify that the annotation applies to deserialization as well as serialization:

Annotation used to indicate that a property should be serialized "unwrapped" -- that is, if it would be serialized as JSON Object, its properties are instead included as properties of its containing Object -- and deserialized reproducing "missing" structure.

[...]

When values are deserialized "wrapping" is applied so that serialized output can be read back in.

M. Justin
  • 14,487
  • 7
  • 91
  • 130
0

For those who googled here like me, trying to resolve issue when deserializing unwrapepd Map, there is a solution with @JsonAnySetter:

public class CountryList
{

    Map<String, Country> countries = new HashMap<>();

    @JsonAnySetter
    public void setCountry(String key, Country value)
    {
        countries.put(key, value);
    }

}
Jan Mares
  • 795
  • 10
  • 22