Kelvin Chung
Kelvin Chung

Reputation: 1377

Using Jackson to deserialize into a Map

I have a JSON object with two attributes: "key" which is a string, and "value" which can be deserialized into a Java bean.

{ "key": "foo", "value": "bar" }

The question is, given a list of such objects, can I deserialize it into a Map?

[{"key": "foo1", "value": "bar1"}, {"key": "foo2", "value": "bar2"}] -> Map<String, String>

Currently using Jackson-databind 2.1

Upvotes: 6

Views: 11229

Answers (4)

Roger Glover
Roger Glover

Reputation: 1

I realize this is fairly late to the game, but here is a solution using more modern Java features, record, List::stream, Map.of(), etc., written up as a JUnit test:

    @Test public void listToMap () throws JsonProcessingException {
        // set up
        String json = """
            [{"key": "foo1", "value": "bar1"}, {"key": "foo2", "value": "bar2"}]""";
        ObjectMapper mapper = new ObjectMapper();

        // solution
        record PseudoEntry(String key, String value) {};
        TypeFactory typeFactory = mapper.getTypeFactory();
        CollectionType startingType = typeFactory.constructCollectionType(List.class, PseudoEntry.class);
        List<PseudoEntry> startingList = mapper.readValue(json, startingType);
        Map<String, String> finalMap = startingList.stream()
            .collect(Collectors.toMap(PseudoEntry::key, PseudoEntry::value));

        // verify
        Assertions.assertEquals(Map.of("foo1", "bar1", "foo2", "bar2"), finalMap);
    }

The PseudoEntry record class matches the key and value fields in the JSON objects.

The startingType object says that the expected result of the JSON deserialization will be of type List<PseudoEntry>.

Thus, the JSON is deserialized as startingList.

Then we use the stream API on that starting list to convert the List items into Map entries in finalMap, by converting each PseudoEntry::key into a Map key and each corresponding PseudoEntry::value into the corresponding Map value.

Thus, the original JSON array of 2 objects becomes a Java List of 2 PseudoEntry objects, which then becomes a Map of two key-value pairs.

Upvotes: 0

marshalThis
marshalThis

Reputation: 1

Had the same problem and was surprised Jackson wasn't able to natively handle it. Solution I went with was to create a custom setter on the object I was trying to marshal into :

public class somePojo {
  private Map<String, String> mapStuff;

  ...

  public void SetMapStuff(List<Map<String, String> fromJackson){
    mapStuff= new HashMap<>();
    for (Map<String, String> pair : fromJackson) {
      put(pair.get("key"), pair.get("value"));
    }
  }
}

Jackson is smart enough to find that setter to and can happily pass it the List.

Upvotes: 0

StaxMan
StaxMan

Reputation: 116610

Since your structure does not match, you have two basic options:

  1. Use two-phase handling: use Jackson to bind into intermediate representation that does match (which could be JsonNode or List<Map<String,Object>>), and then do conversion to desired type with simple code
  2. Write custom serializer and/or deserializer

Jackson does not support extensive structural transformations (there are some simple ones, like @JsonUnwrapped), so this kind of functionality is unlikely to be added to databind module. Although it could be added as an extension module, if these "smart Map" types of structures are commonly used (they seem to be, unfortunately).

Upvotes: 1

Michał Ziober
Michał Ziober

Reputation: 38710

You can easily convert above JSON to List<Map<String, String>>:

import java.util.List;
import java.util.Map;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.CollectionType;

public class JacksonProgram {

    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        CollectionType mapCollectionType = mapper.getTypeFactory().constructCollectionType(List.class, Map.class);
        List<Map<String, String>> result = mapper.readValue(json, mapCollectionType);
        System.out.println(result);
    }
}

Above program prints:

[{key=foo1, value=bar1}, {key=foo2, value=bar2}]

Upvotes: 8

Related Questions