Skip to content

Failure with custom Enum key deserializer, polymorphic typesΒ #1441

@ofiesh

Description

@ofiesh

Normally the JsonParser and the DeserializationContext is passed to a Module's JsonDeserializer.

However, in the MapDeserializer, when deserializing a Map with an Enum key, the KeyDeserializer doesn't accept the JsonParser as an argument:

https://github.com/FasterXML/jackson-databind/blob/master/src/main/java/com/fasterxml/jackson/databind/deser/std/MapDeserializer.java#L453
Object key = keyDes.deserializeKey(keyStr, ctxt);

and the StdKeyDeserializer.DelegatingKD uses the context's parser

https://github.com/FasterXML/jackson-databind/blob/master/src/main/java/com/fasterxml/jackson/databind/deser/std/StdKeyDeserializer.java#L315
Object result = _delegate.deserialize(ctxt.getParser(), ctxt);

When the type info field is missing from the json, the DeserializationContext's JsonParser's token is END_OBJECT (presumably because it nextToken'd through the object to find type and whiffed).

This makes the module fail since the JsonParser in the Module is wrong, i.e. not the same as the JsonParser in the MapDeserializer.

Class:

import com.fasterxml.jackson.annotation.JsonTypeInfo;

import java.util.Map;

import static com.fasterxml.jackson.annotation.JsonTypeInfo.Id.NAME;

@JsonTypeInfo(use = NAME, property = "@type", defaultImpl = SuperType.class)
public class SuperType {
    private Map<SuperTypeEnum, String> someMap;

    public Map<SuperTypeEnum, String> getSomeMap() {
        return someMap;
    }

    public void setSomeMap(Map<SuperTypeEnum, String> someMap) {
        this.someMap = someMap;
    }
}

Enum:

public enum SuperTypeEnum {
    FOO
}

Test:

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.junit.*;

import java.io.IOException;

import static org.junit.Assert.assertEquals;

public class TestDeserializeType {

    @Test
    public void testNoTypeShouldDeserialize() throws IOException {
        String json = "{\"someMap\": {\"FOO\": \"bar\"}}";
        ObjectMapper mapper = new ObjectMapper();
        SuperType superType = mapper.readValue(json, SuperType.class);
        assertEquals("Deserialized someMap.FOO should equal bar", "bar", superType.getSomeMap().get(SuperTypeEnum.FOO));
    }

    @Test
    public void testNoTypeWithModuleShouldDeserialize() throws IOException {
        String json = "{\"someMap\": {\"FOO\": \"bar\"}}";
        ObjectMapper mapper = new ObjectMapper();
        SimpleModule simpleModule = new SimpleModule();
        simpleModule.addDeserializer(SuperTypeEnum.class, new JsonDeserializer<SuperTypeEnum>() {
            @Override
            public SuperTypeEnum deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
                    throws IOException {

                return SuperTypeEnum.valueOf(jsonParser.getText());
            }
        });
        mapper.registerModule(simpleModule);

        SuperType superType = mapper.readValue(json, SuperType.class);
        assertEquals("Deserialized someMap.FOO should equal bar", "bar", superType.getSomeMap().get(SuperTypeEnum.FOO));
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions