Search code examples
javajacksonjackson-databind

How to serialize json to Map?


I need to serialize json to Map. My son looks like the following:

{
   items: [{
      "name": "Test1",
      "value": {
         "id": 1,
         "count": 5
      }
   }]
}

and I have following classes:

public class Value {
    public int id;
    public int count;
}
public class ItemManager {
    public Map<String, Value> items;
}

and I was trying to deserialize it like that:

class Main {
    public static void main(String... args) {
        ObjectMapper mapper = new ObjectMapper();
        ItemManager manager = mapper.read(args[0], ItemManager.class);
    }
}

but I get the following exception:

com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `test.ItemManager` out of START_ARRAY token
 at [Source: (String)"{

I need to put name as a key and value as a value. Can anyone help to do it?


Solution

  • Here is a simple custom Deserializer to do what you want (I’ve used Jackson v2.12.4) :

    import com.fasterxml.jackson.core.JsonParser;
    import com.fasterxml.jackson.core.ObjectCodec;
    import com.fasterxml.jackson.databind.DeserializationContext;
    import com.fasterxml.jackson.databind.JsonNode;
    import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
    
    import java.io.IOException;
    
    public class ItemManagerDeserializer extends StdDeserializer<ItemManager> {
    
        public ItemManagerDeserializer() {
            this(null);
        }
    
        public ItemManagerDeserializer(Class<?> vc) {
            super(vc);
        }
    
        @Override
        public ItemManager deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
            JsonNode node = jp.getCodec().readTree(jp);
            ObjectCodec mapper = jp.getCodec();
    
            ItemManager itemManager = new ItemManager();
            for (JsonNode element : node.get("items")) {
                String key = element.get("name").asText();
                ItemManager.Value value = mapper.treeToValue(element.get("value"), ItemManager.Value.class);
                itemManager.getItems().put(key, value);
            }
    
            return itemManager;
        }
    } 
    

    I’ve created a static Value class in the ItemManager class and instantiated the items property in the constructor (new HashMap<>()).

    Then, you can register and use this Deserializer like so :

    public static void main(String[] args) throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        SimpleModule module = new SimpleModule();
        module.addDeserializer(ItemManager.class, new ItemManagerDeserializer());
        mapper.registerModule(module);
    
        ItemManager itemManager = mapper.readValue(args[0], ItemManager.class);
        itemManager.getItems().forEach((key, value) -> System.out.println("key: " + key + ", value: " + value));
    }