Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot with (de)serialize a List with polymorphic items in Jackson

Tags:

java

json

jackson

I have an issue (de)serializing JSON that is not defined by me.

Here is some code and JSON that explains the issue:

Code:

public static class Base {
    public String klass = "base";
}

public static class SubBase extends Base {
}

public static class Sub1 extends SubBase {
    public Sub1() {
        klass = "Sub1";
    }
}

public static class Sub2 extends SubBase {
    public Sub2() {
        klass = "Sub2";
    }
}

public static class Holder {
    @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.WRAPPER_ARRAY, property="type")
    @JsonSubTypes({@JsonSubTypes.Type (name = "sub1", value = Sub1.class),@JsonSubTypes.Type(name = "sub2", value = Sub2.class)})
    public List<Base> items = new ArrayList<Base>();
}


Holder holder = new Holder();
holder.items.add(new Sub1());
holder.items.add(new Sub1());

mapper.writeValueAsString(holder);

produces

{"items":[["sub1",{"klass":"Sub1"}],["sub1",{"klass":"Sub1"}]]}

If I change the JsonTypeInfo annotation to

    @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.WRAPPER_OBJECT, property="type")

produces

{"items":[{"sub1":{"klass":"Sub1"}},{"sub1":{"klass":"Sub1"}}]}

So far, all is good :)

However, the JSON I'm getting from the server has a slightly different structure:

{"type":"sub1", "items":[{"klass":"Sub1"},{"klass":"Sub1"}]}

Where the type of the items array is defined in the "type" property (note that all items in the "items" array are of the same class).
I just cannot figure out which JsonTypeInfo combination to use to make this happen. I tried setting the 'include' to ".EXTERNAL_PROPERTY", but this doesn't work.
Deserializing using this inclusion gives me "Exception in thread "main" org.codehaus.jackson.JsonGenerationException: Can not write a field name, expecting a value" error message.

My question is: Which annotation do I need to use so that '{"type":"sub1", "items":[{"klass":"Sub1"},{"klass":"Sub1"}]}' will fill the 'items' array with all Sub1 instances based on the "type" property of the Holder?
If this is just not possible, is there another way to accomplish this (without the need of an custom serializer for Holder; a custom serializer just for the 'items' array would be fine)?

Thanks!

like image 743
Streets Of Boston Avatar asked Feb 22 '13 15:02

Streets Of Boston


1 Answers

There is no way to map JSON you show automatically; it is not one of 4 types supported by Jackson.

If you can't make server produce more standard structure (for which automatic support exists), you will need to write a custom deserializer; or to do data-binding in two steps, first into an intermediate easily mappable structure (like JsonNode or Map) and then from that into desired structure manually extracting type, using that (for example, with ObjectMapper.convertValue(object, resultType).

like image 130
StaxMan Avatar answered Nov 09 '22 04:11

StaxMan