jackson-databind icon indicating copy to clipboard operation
jackson-databind copied to clipboard

Polymorphism with aliases fails to utilize alias fields

Open Dragas opened this issue 2 years ago • 2 comments

As per thread in the mailing list:

I'm trying to migrate a makeshift polymorphic custom deserializer (which i spawned because I hadn't read the docs). The deserializer originally worked like the following annotation combo as per Polymorphic Deserialization usage

public class Container {
    private String id;
    @JsonTypeInfo(use = Id.NAME, include = As.EXTERNAL_PROPERTY, property = "id")
    @JsonSubTypes({
            @Type(name = "FOO", value = Foo.class),
            @Type(name = "NAR", value = Nar.class)
    })
    private Object target;
}

Getters and setters omitted for brevity.

Basically original implementation could set the target field value as particular type depending on id field. But in addition to that, it could determine which field to read from, since the input source (sadly) only has one constant field (id) and target field's name would change depending on id field. If I was not using typeinfo and subtypes annotation combo, the annotation for aliases would be

@JsonAlias({"fooValue", "narValue"})

On their own, the two features work fine, but when you would try to use the two together, polymorphic deserializer is incapable of determining the source value field via aliases. As a result, the following exception is thrown

Exception in thread "main" com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "narValue" (class asdf.Main$Container), not marked as ignorable (one known property: "id"])
 at [Source: (String)"{ "id": "NAR", "narValue": {}}"; line: 1, column: 29] (through reference chain: asdf.Main$Container["narValue"])
	at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:61)
	at com.fasterxml.jackson.databind.DeserializationContext.handleUnknownProperty(DeserializationContext.java:987)
	at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:1974)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(BeanDeserializerBase.java:1701)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeWithExternalTypeId(BeanDeserializer.java:998)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeWithExternalTypeId(BeanDeserializer.java:950)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeWithExternalTypeId(BeanDeserializer.java:943)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:360)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:195)
	at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:322)
	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4593)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3548)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3516)
	at asdf.Main.main(Main.java:17)

It would be nice if I could specify field name per id type that would deserialize from, but I'm willing to sacrifice that in order to get this working to begin with.

In this minimal working sample I'm using jackson 2.12.3 with Oracle JDK 11.0.10u8. In the project that would be affected I'm using jackson 2.9.8 with Oracle JDK 11.0.10u8.

Below is the minimum working snippet to replicate my issue

import com.fasterxml.jackson.annotation.JsonAlias;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class Main {

    public static void main(String[] args) throws JsonProcessingException {
        String json = "{ \"id\": \"NAR\", \"narValue\": {}}";
        ObjectMapper mapper = new ObjectMapper();
        Container container = mapper.readValue(json, Container.class);
        System.out.println(container.getTarget()); // expected value is Nar@[hashcode]
    }

    public static class Nar {

        private String dar;
        private String zar;

        public String getDar() {
            return dar;
        }

        public void setDar(String dar) {
            this.dar = dar;
        }

        public String getZar() {
            return zar;
        }

        public void setZar(String zar) {
            this.zar = zar;
        }
    }

    public static class Foo {
        private String bar;
        private String zar;

        public String getBar() {
            return bar;
        }

        public void setBar(String bar) {
            this.bar = bar;
        }

        public String getZar() {
            return zar;
        }

        public void setZar(String zar) {
            this.zar = zar;
        }
    }

    public static class Container {

        private String id;
        @JsonTypeInfo(use = Id.NAME, include = As.EXTERNAL_PROPERTY, property = "id")
        @JsonSubTypes({
                @Type(name = "FOO", value = Foo.class),
                @Type(name = "NAR", value = Nar.class)
        })
        @JsonAlias({
                "fooValue",
                "narValue"
        })
        private Object target;

        public String getId() {
            return id;
        }

        public void setId(String id) {
            this.id = id;
        }

        public Object getTarget() {
            return target;
        }

        public void setTarget(Object target) {
            this.target = target;
        }
    }
}

Dragas avatar Jul 13 '21 07:07 Dragas