question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

@JsonTypeInfo with EXTERNAL_PROPERTY doesn't handle arrays of polymorphic types

See original GitHub issue

While the JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY works fine normally, there is an issue when using it along with @JsonTypeInfo and its JsonTypeInfo.As.EXTERNAL_PROPERTY feature (regular PROPERTY works fine). Below is an example test case to illustrate the issue:

    public static class Foo {
        public String msg;
    }

    public static class FooA extends Foo {
        public String hey;
    }

    public static class FooB extends Foo {
        public String whoa;
    }

    public static class Holder {
        public String footype;

        @JsonTypeInfo(use      = JsonTypeInfo.Id.NAME,
                      include  = JsonTypeInfo.As.EXTERNAL_PROPERTY,
                      property = "footype")
        @JsonSubTypes({@JsonSubTypes.Type(value=FooA.class, name="a"),
                       @JsonSubTypes.Type(value=FooB.class, name="b") })
        @JsonFormat(with=JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
        public List<Foo> foo;
        public Foo other;
    }

    @Test
    public void testSVA() throws Exception
    {
        final InputStream stream = TestSingleValueArray.class.getResourceAsStream("footest.json");
        Scanner s = new Scanner(stream);
        final String json = s.useDelimiter("\\Z").next();
        s.close();

        final ObjectMapper mapper = new ObjectMapper();

        final Holder holder = mapper.readValue(json, Holder.class);
    }

I expected the above test to combine the external property and array features, but it instead throws the following error:

com.fasterxml.jackson.databind.JsonMappingException: Unexpected token (START_OBJECT), expected START_ARRAY: need JSON Array to contain As.WRAPPER_ARRAY type information for class TestSingleValueArray$Foo
 at [Source: {
    "footype": "a",
    "foo": {
        "msg": "Hello World",
        "hey": "there"
    },
    "other": {
        "msg": "Goodbye"
    }
}; line: 3, column: 12] (through reference chain: Holder["foo"]->java.lang.Object[0])
    at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:216)
    at com.fasterxml.jackson.databind.DeserializationContext.wrongTokenException(DeserializationContext.java:962)
    at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer._locateTypeId(AsArrayTypeDeserializer.java:127)
    at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer._deserialize(AsArrayTypeDeserializer.java:93)
    at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer.deserializeTypedFromObject(AsArrayTypeDeserializer.java:58)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeWithType(BeanDeserializerBase.java:1017)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.handleNonArray(CollectionDeserializer.java:341)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:259)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:249)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:26)
    at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:490)
    at com.fasterxml.jackson.databind.deser.impl.FieldProperty.deserializeAndSet(FieldProperty.java:101)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:260)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:125)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3788)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2779)
    at TestSingleValueArray.testSVA(TestSingleValueArray.java:63)

Here is the input used to produce the above error:

{
    "footype": "a",
    "foo": {
        "msg": "Hello World",
        "hey": "there"
    },
    "other": {
        "msg": "Goodbye"
    }
}

Moving the footype field into Foo and switching from EXTERNAL_PROPERTY to a plain PROPERTY fixes the error, but of course is not quite the same.

Thanks in advance for your help!

Issue Analytics

  • State:open
  • Created 8 years ago
  • Comments:7 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
cowtowncodercommented, Apr 19, 2022

@msmerc Unfortunately I don’t think I will have time to dig into this issue, although ideally would of course love to help.

The big problem, I think, is that EXTERNAL_PROPERTY basically CANNOT work for “real” arrays (that is, Collections and arrays serialized as JSON Arrays) because there is no place in JSON Array to add properties. It could, in theory, work for the special case of “unwrapped” single-element array, but that seems fragile as it would only work for that one special case.

I think that one good improvement would be for Jackson to detect this usage attempt at fail immediately with proper exception message (cannot use this combination of features), to at least let user know what is the issue.

0reactions
msmerccommented, Apr 12, 2022

So it seems the first problem is that collection / list properties annotated this way aren’t picked up and included in the _externalTypeHnadler because they don’t have a valueTypeDeserialiser. See https://github.com/FasterXML/jackson-databind/blob/92f809dd161a80d0d5c519f78ea18443715970ad/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java#L592

Though I’m guessing this is just the tip of the iceberg and there would be more work after this.

@cowtowncoder Might it be possible to get some love for this issue? I’m guessing it’s unlikely!

Read more comments on GitHub >

github_iconTop Results From Across the Web

Why does Jackson polymorphic serialization not work in lists?
I'm doing polymorphic serialization and it works perfectly when an object is on its own. But if you put the same object into...
Read more >
Using @JsonTypeInfo annotation to handle polymorphic types
When used on properties (fields, methods), this annotation applies to values. That means when used on collection types (Collection, Map, arrays) ...
Read more >
JsonTypeInfo (Jackson-annotations 2.14.0 API) - FasterXML
When used for properties (fields, methods), this annotation applies to values: so when applied to structure types (like Collection , Map , arrays),...
Read more >
Polymorphic resolution without annotations, looking for ...
I am currently trying to handle polymorphic resolution without using the ... No, kind of type identifier to use is specified by @JsonTypeInfo...
Read more >
Jackson-js: Powerful JavaScript decorators to ... - ITNEXT
jackson-js is heavily inspired by the famous Java FasterXML/jackson library. ... polymorphic type handling decorators: @JsonTypeInfo , @JsonSubTypes , and ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found