9

I have to serialize a simple integer-to-string map as JSON and then read it back. The serialization is pretty simple, however since JSON keys must be strings the resulting JSON looks like:

{
  "123" : "hello",
  "456" : "bye",
}

When I read it using code like:

new ObjectMapper().readValue(json, Map.class)

I get Map<String, String> instead of Map<Integer, String> that I need.

I tried to add key deserializer as following:

    Map<Integer, String> map1 = new HashMap<>();
    map1.put(1, "foo");
    map1.put(2, "bar");


    ObjectMapper mapper = new ObjectMapper();
    SimpleModule module = new SimpleModule();
    module.addKeyDeserializer(Integer.class, new KeyDeserializer() {
        @Override
        public Object deserializeKey(String key, DeserializationContext ctxt) throws IOException, JsonProcessingException {
            System.out.println("deserialize " + key);
            return Integer.parseInt(key);
        }
    });

    mapper.registerModule(module);
    String json = mapper.writeValueAsString(map1);


    Map map2 = mapper.readValue(json, Map.class);
    System.out.println(map2);
    System.out.println(map2.keySet().iterator().next().getClass());

Unfortunately my key deserialzier is never called and map2 is in fact Map<String, String>, so my example prints:

{1=foo, 2=bar}
class java.lang.String

What am I doing wrong and how to fix the problem?

AlexR
  • 114,158
  • 16
  • 130
  • 208

1 Answers1

15

Use

Map<Integer, String> map2 = 
        mapper.readValue(json, new TypeReference<Map<Integer, String>>(){});

or

    Map<Integer, String> map2 = 
        mapper.readValue(json, TypeFactory.defaultInstance()
                         .constructMapType(HashMap.class, Integer.class, String.class));

Your program will output below text:

deserialize 1
deserialize 2
{1=foo, 2=bar}
class java.lang.Integer
Wand Maker
  • 18,476
  • 8
  • 53
  • 87
  • Cool, thanks a lot. This is surely the best solution for me. However it is still interesting why my solution does not work. – AlexR Jul 05 '15 at 14:04
  • 4
    Jackson did not know the types of key and values of your Map. It need to be told about that. If you were de-serializing to a POJO, and that POJO had a Map, then, Jackson seems to know what to do. However, when parsing Map directly, the above seems to be the way to inform Jackson about it – Wand Maker Jul 05 '15 at 14:21
  • I see. Now it is clear. Thanks a lot for the explanations. – AlexR Jul 05 '15 at 14:24
  • 1
    Even if I use `map1` in left hand side of `mapper.readValue(json, Map.class);` then why it does not complain? The key of map1 is `Integer` but when I use `getClass()` it is printed as `String`. What happens here during deserialiazation? – Thiyagu Oct 23 '15 at 14:28