Reputation: 1377
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
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
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
Reputation: 116610
Since your structure does not match, you have two basic options:
JsonNode
or List<Map<String,Object>>
), and then do conversion to desired type with simple codeJackson 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
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