8

What I Want to Do

I want to use Jackson to deserialize a polymorphic type, using the standard @JsonTypeInfo annotation as follows:

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, 
              include = As.EXISTING_PROPERTY, 
              property = "identifier")
@JsonSubTypes({@Type(value = A.class, name = "A"),
               @Type(value = B.class, name = "B")})
abstract Class Base {}

Class A implements Base {
    public String identifier = "A";
}

Class B implements Base {
    public String identifier = "B";
}

Class Decorated {
    public String decoration = "DECORATION";

    @JsonUnwrapped
    public Base base;
}

/* 
    Serialized instance of Decorated WITHOUT @JsonUnwrapped:
    {
        "decoration" : "DECORATION",
        "base" : {
            "identifier" : "A"
        }
    }

    Serialized instance of Decorated WITH @JsonUnwrapped:
    {
        "decoration" : "DECORATION",
        "identifier" : "A"
    }
*/

Related post: Deserialize JSON with Jackson into Polymorphic Types - A Complete Example is giving me a compile error

This can normally be deserialized by Jackson as follows:

public Object deserialize(String body, Class clazz) {
    ObjectMapper objectMapper = new ObjectMapper();
    return objectMapper.readValue(body, clazz);
}

(And this would work if the @JsonUnwrapped annotation were removed)


The Problem

Polymorphic types do not play well with Jackson's @JsonUnwrapped annotation, as discussed in this Jira ticket from 2012:

http://markmail.org/message/pogcetxja6goycws#query:+page:1+mid:pogcetxja6goycws+state:results

Handle polymorphic types with @JsonUnwrapped

Agreed - while fixing things is obviously preferable, improving error messages would be useful if that can't be done.

Unwrapping is one of features where implementations gets complicated enough that any bugs cropping up (on deserialization esp) tend to be antibiotic-resistant...

Hardly encouraging.

Three years later:

http://markmail.org/message/cyeyc2ousjp72lh3

Handle polymorphic types with @JsonUnwrapped

Resolution: Won't Fix

Damn.

So, is there any way to coax Jackson into giving me this behaviour without modifying deserialize() or removing the @JsonUnwrapped annotation?

Zaaier
  • 685
  • 8
  • 22
  • Have you managed to solve this or to find a workaround? – Dimebag Jul 28 '17 at 13:21
  • I haven't, no. You can always implement a custom serializer that delegates serialization of Base back to Jackson after writing the 'decoration' field. – Zaaier Jul 28 '17 at 18:29

1 Answers1

4

My SinglePolyUnwrappedDeserializer from this Gist can handle a single polymorphic @JsonUnwrapped property. It's in Kotlin, but can easily be ported to Java if needed. Example:

@JsonTypeInfo(
    use = JsonTypeInfo.Id.NAME,
    include = JsonTypeInfo.As.PROPERTY,
    property = "type"
)
@JsonSubTypes(
    JsonSubTypes.Type(value = A::class, name = "a"),
    JsonSubTypes.Type(value = B::class, name = "b")
)
abstract class Base

data class A(val x: Int) : Base()

data class B(val y: Boolean) : Base()

@JsonDeserialize(using = SinglePolyUnwrappedDeserializer::class)
data class C(val a: String, @JsonUnwrapped val b: Base)

AFAIK, all combinations of other annotations are supported. The only limitation is that there is exactly one @JsonUnwrapped property.

If you also need a generic serializer for polymorphic @JsonUnwrapped, you can write it yourself very easily without any reflection or introspection: just merge the ObjectNode of the inner object onto the ObjectNode of the containing object.

Anton3
  • 577
  • 4
  • 14
  • Can you elaborate on serialization? A JsonSerializer has a JsonGenerator, so it's not clear how you serialize the inner object to an ObjectNode? – Tom Quarendon Jan 22 '20 at 09:18
  • @TomQuarendon `(gen.codec as ObjectMapper).valueToTree(inner)` (Sorry for not answering for months, lol. At the time I thought it's obvious enough, and I only needed the deserializer) – Anton3 Apr 04 '20 at 18:20