Search code examples
javajsonjacksonguavajackson-modules

Jackson ObjectMapper wraps every element with square brackets (Even single values)


I have two Map objects where I merge them into Guava's Multimap. After merging I am trying to serialize the final map by jackson ObjectMapper with GuavaModule (otherwise it doesn't serialize properly). However, the issue is every value of a key is printed as an element of a list even when there is only one value (objectmapper wraps every value with square brackets). The basic output I get is as below,

{
    "address": {
        "city": ["san francisco"]
    },
    "company": {
        "employees": [
            [{
                "name": "David",
                "lastname": "Foobar",
                "age": 22
            }, {
                "name": "Michael",
                "lastname": "FizBuzz",
                "age": 35
            }]
        ]
    },
    "config": {
        "key1": ["value1", "value3"],
        "key2": ["value2"],
        "key4": ["value4"]
    }
}

As you can see "address" -> "city" -> "san francisco" is wrapped with square brackets, also company -> employees is wrapped twice Additionally, I have tried SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED, true future but nothing is changed.

    Person person = new Person();
    person.setName("David");
    person.setLastname("Foobar");
    person.setAge(22);

    Person person2 = new Person();
    person2.setName("Michael");
    person2.setLastname("FizBuzz");
    person2.setAge(35);

    Map<String, Map<String, Object>> map1 = new ImmutableMap.Builder<String, Map<String, Object>>()
            .put("config", ImmutableMap.of(
                    "key1", "value1",
                    "key2", "value2"))
            .put("address", ImmutableMap.of("city", "san francisco"))
            .put("company", ImmutableMap.of("employees", Arrays.asList(person, person2)))
            .build();

    Map<String, Map<String, Object>> map2 = new ImmutableMap.Builder<String, Map<String, Object>>()
            .put("config", ImmutableMap.of(
                    "key1", "value3",
                    "key4", "value4"))
            .build();

    Map<String, Multimap<String, Object>> merged = new HashMap<>();
    BiFunction<Multimap<String, Object>,
                Multimap<String, Object>,
                Multimap<String, Object>> remappingFunction = (value1, value2) -> {
        Multimap<String, Object> multimap = HashMultimap.<String, Object>create();
        multimap.putAll(value1);
        multimap.putAll(value2);
        return multimap;
    };


    map1.forEach((key, value) -> merged.merge(key, Multimaps.forMap(value), remappingFunction));
    map2.forEach((key, value) -> merged.merge(key, Multimaps.forMap(value), remappingFunction));

    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.registerModule(new GuavaModule());
    objectMapper.configure(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED, true);
    String str = objectMapper.writeValueAsString(merged);
    System.out.print(str);
}

I appreciate for any help. Thanks


Solution

  • According to the Jackson documentation, the MultimapSerializer "encloses all value sets in JSON Array, regardless of number of values". I believe this means it is effectively ignoring the WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED feature.

    You can avoid this problem by converting all your inner Multimap<String, Object> instances into standard Map<String, Collection<Object>> instances before serializing. You can do this by using Multimap.asMap():

    Map<String, Map<String, Collection<Object>>> newMerged = new HashMap<>();
    merged.forEach((key, value) -> newMerged.put(key, value.asMap()));
    String newStr = objectMapper.writeValueAsString(newMerged);
    System.out.println(newStr);
    

    This prints the output that you are expecting:

    {"address":{"city":"san francisco"},"company":{"employees":[{"name":"David","lastname":"Foobar","age":22},{"name":"Michael","lastname":"FizBuzz","age":35}]},"config":{"key1":["value1","value3"],"key2":"value2","key4":"value4"}}