Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Failure with custom Enum key deserializer, polymorphic types #1441

Closed
ofiesh opened this issue Nov 2, 2016 · 2 comments
Closed

Failure with custom Enum key deserializer, polymorphic types #1441

ofiesh opened this issue Nov 2, 2016 · 2 comments
Milestone

Comments

@ofiesh
Copy link

ofiesh commented Nov 2, 2016

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));
    }
}
@cowtowncoder
Copy link
Member

Thank you for reporting this. It sounds like work-around used to allow some value deserializers to be used as key deserializers has some issues.

@cowtowncoder cowtowncoder modified the milestones: 2.8, 2.8.5 Nov 3, 2016
@cowtowncoder cowtowncoder changed the title When json type info is missing from json, Modules for Enums as keys of Maps always have END_TOKEN in the JsonParser Failure with custom Enum key deserializer, polymorphic types Nov 3, 2016
@cowtowncoder
Copy link
Member

Wow. Not sure why I implemented delegating key deserializer that way; it's wrong -- should not delegate original parser in general, and in this case parser would be wrong due to buffering needed for type id.
Anyway: better way is to construct bogus TokenBuffer as JsonParser and add key (that has already been accessed and is passed as the argument), give that parser; it's safer and necessary here.

Thank you for reporting this, providing test case! Fixed for 2.8.5, to be released relatively soon (within next week or two).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants