Search code examples
javajsonserializationmultimap

Jackson JSON - Deserialize Commons MultiMap


i want to serialize and deserialize a MultiMap (Apache Commons 4) using JSON.

Piece of code to test:

MultiMap<String, String> map = new MultiValueMap<>();
map.put("Key 1", "Val 11");
map.put("Key 1", "Val 12");
map.put("Key 2", "Val 21");
map.put("Key 2", "Val 22");

ObjectMapper mapper = new ObjectMapper();
String jsonString = mapper.writeValueAsString(map);
MultiMap<String, String> deserializedMap = mapper.readValue(jsonString, MultiValueMap.class);

The serialization works fine and results in a format I would expect:

{"Key 1":["Val 11","Val 12"],"Key 2":["Val 21","Val 22"]}

Unfortunately the deserialization produces a result that is not the way it should look like: After the deserialization, the Multimap contains an ArrayList inside an ArrayList for the values of a key, not an single ArrayList for the key containing the values.

This result is produced due to the fact that the put() method of the multi map is called to add the array found in the json string, as the MultiMap implements the Map interface.

The MultiMap implementation itself again then creates an ArrayList if a new value is put to a non existing key.

Is there any way to circumvent this?

Thank you for your help!


Solution

  • Being assured from Oxford dictionary that circumvent means to "find a way around (an obstacle)", here is a simple work around.

    First I created a method that generate the same MultiValueMap as yours above. And I use the same approach to parse it as a json string.

    I then created the following deserialization method

    public static MultiMap<String,String> doDeserialization(String serializedString) throws JsonParseException, JsonMappingException, IOException {
    
        ObjectMapper mapper = new ObjectMapper();
        Class<MultiValueMap> classz = MultiValueMap.class;
        MultiMap map = mapper.readValue(serializedString, classz);
        return (MultiMap<String, String>) map;
    
    
    }
    

    Of course this alone falls in the exact issue you mentionned above, therefore I created the doDeserializationAndFormatmethod: it will iterate through each "list inside a list" correponding to a given key and associate one by one the values to the key

    public static MultiMap<String, String> doDeserializationAndFormat(String serializedString) throws JsonParseException, JsonMappingException, IOException {
        MultiMap<String, String> source = doDeserialization(serializedString);
        MultiMap<String, String> result  =  new MultiValueMap<String,String>();
        for (String key: source.keySet()) {
    
    
            List allValues = (List)source.get(key);
            Iterator iter = allValues.iterator();
    
            while (iter.hasNext()) {
                List<String> datas = (List<String>)iter.next();
    
                for (String s: datas) {
                    result.put(key, s);
                }
            }
    
        }
    
        return result;
    
    }
    

    Here is a simple call in a main method:

    MultiValueMap<String,String> userParsedMap = (MultiValueMap)doDeserializationAndFormat(stackMapSerialized);
    System.out.println("Key 1 = " + userParsedMap.get("Key 1") );
    System.out.println("Key 2 = " + userParsedMap.get("Key 2") );
    

    json to multivaluemap

    Hope this helps.