1. Overview
In this short tutorial, we’ll explore how to solve the Jackson exception JsonMappingException: Can not deserialize instance of java.util.HashMap out of START_ARRAY token.
First, we’ll elucidate the root cause behind the exception. Then, we’ll illustrate how to reproduce it using a practical example and finally, how to fix it.
2. Understanding the Exception
In short, Jackson throws JsonMappingException to indicate a mapping error when deserializing a JSON string. Furthermore, the message “Can not deserialize instance of java.util.HashMap out of START_ARRAY token” signals a mismatch between the expected data structure and the actual JSON string.
Here, the mismatch occurs because Jackson expects a List and not a HashMap. The deserialization process doesn’t know how to convert the specified JSON array into a HashMap. Hence, the exception.
3. Practical Example
Now that we understand what causes Jackson to fail with JsonMappingException, let’s see how to reproduce it using a practical example.
To keep things simple, let’s assume we have a JSON array where each element represents a person defined by a first name and a last name:
[
{
"firstName":"Abderrahim",
"lastName":"Azhrioun"
},
{
"firstName":"Nicole",
"lastName":"Smith"
}
]
Now, attempting to deserialize the JSON array directly into a Map will lead to JsonMappingException:
@Test
public void givenJsonArray_whenDeserializingToMap_thenThrowException() {
final String json = "[{\"firstName\":\"Abderrahim\",\"lastName\":\"Azhrioun\"}, {\"firstName\":\"Nicole\",\"lastName\":\"Smith\"}]";
final ObjectMapper mapper = new ObjectMapper();
Exception exception = assertThrows(JsonMappingException.class, () -> mapper.readValue(json, HashMap.class));
assertTrue(exception.getMessage()
.contains(
"Cannot deserialize value of type `java.util.HashMap<java.lang.Object,java.lang.Object>` from Array value (token `JsonToken.START_ARRAY`)"));
}
As we can see, Jackson fails with “Cannot deserialize value of type HashMap from Array value (token `JsonToken.START_ARRAY`)” because the ObjectMapper doesn’t know how to deserialize the given JSON array directly into a HashMap.
4. Solution
By default, Jackson expects a List when deserializing a JSON array. So, the most straightforward solution would be to use a List of Map instead of a single Map.
So, let’s see it in action:
@Test
public void givenJsonArray_whenDeserializingToListOfMap_thenConvert() throws JsonProcessingException {
final List<Map<String, String>> expectedListOfMaps = Arrays.asList(Map.of("firstName", "Abderrahim", "lastName", "Azhrioun"),
Map.of("firstName", "Nicole", "lastName", "Smith"));
final String json = "[{\"firstName\":\"Abderrahim\",\"lastName\":\"Azhrioun\"}, {\"firstName\":\"Nicole\",\"lastName\":\"Smith\"}]";
final ObjectMapper mapper = new ObjectMapper();
List<Map<String, String>> personList = mapper.readValue(json, new TypeReference<>() {});
assertThat(expectedListOfMaps).isEqualTo(personList);
}
As shown above, we used TypeReference to instruct Jackson to deserialize the JSON array into a List of Map<String, String>.
Alternatively, we can create a custom class to represent each JSON element. For instance, let’s consider the Person class:
public class Person {
private String firstName;
private String lastName;
// default constructor, full parameterized constructor, getters and setters
}
The basic idea here is to use the Person class instead of Map<String, String>. That way, each JSON array’s element will be mapped to a Person object. So, let’s confirm this using another test case:
@Test
public void givenJsonArray_whenDeserializingToListOfCustomObjects_thenConvert() throws JsonProcessingException {
final List<Person> expectedPersonList = Arrays.asList(new Person("Abderrahim", "Azhrioun"), new Person("Nicole", "Smith"));
final String json = "[{\"firstName\":\"Abderrahim\",\"lastName\":\"Azhrioun\"}, {\"firstName\":\"Nicole\",\"lastName\":\"Smith\"}]";
final ObjectMapper mapper = new ObjectMapper();
List<Person> personList = mapper.readValue(json, new TypeReference<>() {});
assertThat(expectedPersonList).usingRecursiveComparison()
.isEqualTo(personList);
}
Here, we tell Jackson to return a List of Person objects instead of a List of Map<String, String>.
5. Conclusion
In this short article, we explained the main cause of the Jackson exception JsonMappingException: Can not deserialize instance of java.util.HashMap out of START_ARRAY token. Along the way, we demonstrated how to reproduce the exception and how to fix it.
As always, the full source code of the examples is available over on GitHub.