Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deserializing non-string map keys with Jackson

Tags:

I have a a map that looks like this:

public class VerbResult {     @JsonProperty("similarVerbs")     private Map<Verb, List<Verb>> similarVerbs; } 

My verb class looks like this:

public class Verb extends Word {     @JsonCreator     public Verb(@JsonProperty("start") int start, @JsonProperty("length") int length,             @JsonProperty("type") String type, @JsonProperty("value") VerbInfo value) {         super(length, length, type, value);     }     //... } 

I want to serialize and deserialize instances of my VerbResult class, but when I do I get this error: Can not find a (Map) Key deserializer for type [simple type, class my.package.Verb]

I read online that you need to tell Jackson how to deserialize map keys, but I didn't find any information explaining how to go about doing this. The verb class needs to be serialized and deserialzed outside of the map as well, so any solution should preserve this functionality.

Thank you for your help.

like image 816
Max Avatar asked Jun 28 '12 14:06

Max


People also ask

How do you make a map object serializable?

We are using writeObject() method of ObjectOutputStream to serialize HashMap in Java. In the following program, we save the hashmap content in a serialized newHashMap file. Once you run the following code, a newHashMap file will be created. This file is used for deserialization in the next upcoming program.

Does Jackson use Java serialization?

Note that Jackson does not use java.

How does Jackson Deserialization work?

Jackson uses default (no argument) constructor to create object and then sets value using setters. so you only need @NoArgsConstructor and @Setter.


2 Answers

After a day of searching, I came across a simpler way of doing it based on this question. The solution was to add the @JsonDeserialize(keyUsing = YourCustomDeserializer.class) annotation to the map. Then implement your custom deserializer by extending KeyDeserializer and override the deserializeKey method. The method will be called with the string key and you can use the string to build the real object, or even fetch an existing one from the database.

So first in the map declaration:

@JsonDeserialize(keyUsing = MyCustomDeserializer.class) private Map<Verb, List<Verb>> similarVerbs; 

Then create the deserializer that will be called with the string key.

public class MyCustomDeserializer extends KeyDeserializer {     @Override     public MyMapKey deserializeKey(String key, DeserializationContext ctxt) throws IOException, JsonProcessingException {         //Use the string key here to return a real map key object         return mapKey;     } } 

Works with Jersey and Jackson 2.x

like image 97
Japheth Ongeri - inkalimeva Avatar answered Sep 17 '22 14:09

Japheth Ongeri - inkalimeva


As mentioned above the trick is that you need a key deserializer (this caught me out as well). In my case a non-String map key was configured on my class but it wasn't in the JSON I was parsing so an extremely simple solution worked for me (simply returning null in the key deserializer).

public class ExampleClassKeyDeserializer extends KeyDeserializer {     @Override     public Object deserializeKey( final String key,                                   final DeserializationContext ctxt )        throws IOException, JsonProcessingException     {         return null;     } }  public class ExampleJacksonModule extends SimpleModule {     public ExampleJacksonModule()     {         addKeyDeserializer(             ExampleClass.class,             new ExampleClassKeyDeserializer() );     } }  final ObjectMapper mapper = new ObjectMapper(); mapper.registerModule( new ExampleJacksonModule() ); 
like image 36
jdt Avatar answered Sep 16 '22 14:09

jdt