Skip to content

Deserialization of generic type with Map.class  #1297

@arekgabiga

Description

@arekgabiga

There is some problem with deserialization of generic types with Map.class. Bug scenario looks like this:

package tst;

import java.util.HashMap;
import java.util.Map;

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;

public class MapTest {
    public static class Wrapper<T> {
        private T content;

        public T getContent() {
            return content;
        }

        public void setContent(T content) {
            this.content = content;
        }
    }

    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        Wrapper<Map> wrapper = buildSample();
        String wrapperJson = mapper.writeValueAsString(wrapper);

        //throws NPE
        JavaType type = mapper.getTypeFactory().constructParametricType(Wrapper.class, Map.class);
        Wrapper<Map> result = mapper.readValue(wrapperJson, type);
        System.out.println(result.getContent());
    }

    private static Wrapper<Map> buildSample() {
        Map map = new HashMap<>();
        map.put("a", "b");

        Wrapper<Map> wrapper = new Wrapper<>();
        wrapper.setContent(map);
        return wrapper;
    }
}

I've checked it with version 2.7.x and 2.8.0 and that code throws NPE (stacktrace from 2.8.0):

Exception in thread "main" java.lang.NullPointerException
    at com.fasterxml.jackson.databind.type.TypeFactory._mapType(TypeFactory.java:1022)
    at com.fasterxml.jackson.databind.type.TypeFactory._fromWellKnownClass(TypeFactory.java:1313)
    at com.fasterxml.jackson.databind.type.TypeFactory._fromClass(TypeFactory.java:1262)
    at com.fasterxml.jackson.databind.type.TypeFactory.constructParametricType(TypeFactory.java:885)

However version 2.4.1 seems to work fine. What I can tell, problem exists only when Map.class is provided. What is more, when I construct JavaType mapType = mapper.getTypeFactory().constructMapType(Map.class, String.class, String.class); deserialize, and then try to deserialize using generic Map.class it works as in the sample below:

package tst;

import java.util.HashMap;
import java.util.Map;

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;

public class MapTest {
    public static class Wrapper<T> {
        private T content;

        public T getContent() {
            return content;
        }

        public void setContent(T content) {
            this.content = content;
        }
    }

    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        Wrapper<Map> wrapper = buildSample();
        String wrapperJson = mapper.writeValueAsString(wrapper);

        JavaType mapType = mapper.getTypeFactory().constructMapType(Map.class, String.class, String.class);
        JavaType resultType = mapper.getTypeFactory().constructParametricType(Wrapper.class, mapType);
        Wrapper<Map> result1 = mapper.readValue(wrapperJson, resultType);
        System.out.println(result1.getContent());

        //does not throw NPE now
        JavaType type = mapper.getTypeFactory().constructParametricType(Wrapper.class, Map.class);
        Wrapper<Map> result2 = mapper.readValue(wrapperJson, type);
        System.out.println(result2.getContent());
    }

    private static Wrapper<Map> buildSample() {
        Map map = new HashMap<>();
        map.put("a", "b");

        Wrapper<Map> wrapper = new Wrapper<>();
        wrapper.setContent(map);
        return wrapper;
    }
}

Looking into the code, Map type is recognized properly, however the code assumes that TypeBindings exist, which is wrong at that state. Situation changes is the second scenario, because Map type gets loaded into the typeCache first and then it is used whenever Map type needs to be constructed.

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