Search code examples
javajacksonjackson-databind

How to serialize/deserialize object to Map


I have one specific case. I need to serialize/deserialize an object to Map<String, Object>. I have a class that looks like the following:

public class Data {
    public String name;
    public Map<String, Object> options = new HashMap<>();
}

I can put to this options objects of any type. For instance:

public class Option {
    public int id;
    ...
}

public class TestOpt {
    public name;
    ...
}

and I try to serialize and deserialize it:

public static void main(String... args) {
    ObjectMapper mapper = new ObjectMapper();
    Option o = new Option();
    o.id = 1;
    TestOpt t = new TestOpt();
    t.name = "fff";
    Data data = new Data();
    data.name = "data";
    data.options.put("o", o);
    data.options.put("t", t);
    String json = mapper.writeValueAsString(data);

    Data d1 = mapper.readValue(json, Data.class);
    // I get error because options.get("o") contains LinkedHashMap instead of Option.class
    System.out.println(((Option)d1.options.get("o")).id);
}

How can I fix this issue?


Solution

  • The value of the serialized json is

    {"name":"data","options":{"t":{"name":"fff"},"o":{"id":1}}}
    

    So, the problem is that the object mapper has no way to tell that the o value inside the json is an Option. The best guess is that it could be a map and thus it is deserialized as a LinkedHashMap.

    If you are sure that the element o is an Option, you can convert the value using an object mapper:

    Option option = mapper.convertValue(d1.options.get("o"), Option.class);
    

    But please note, that this means that the value is again serialized and then deserialized using the right type information. You can do that, but it is not a good solution.

    If it is possible, a better way would be to change your model from a generic map to a specific class that contains the type information:

    class Data {
        public String name;
        public DataOptions options = new DataOptions();
    }
    
    class DataOptions {
        public Option o;
        public TestOpt t;
    }
    

    Serializing this model has the same json representation as the model using a map, and the model can be used to deserialize the json from your example.