1. 概述
本教程将深入探讨如何解决Jackson异常:JsonMappingException: Can not deserialize instance of java.util.HashMap out of START_ARRAY token
。我们将:
- 分析异常的根本原因
- 通过实际代码示例复现问题
- 提供两种解决方案
2. 异常原理
Jackson抛出JsonMappingException
的核心原因是目标数据结构与JSON数据格式不匹配。具体来说:
- 异常信息中的
START_ARRAY token
表示JSON数据以数组[ ]
开头 - 但Jackson预期反序列化的是
HashMap
(即JSON对象{ }
) - 根本矛盾:Jackson无法将JSON数组直接映射为HashMap
⚠️ 这种错误常发生在以下场景:
- 前端返回数组格式数据
- 后端错误地将其反序列化为Map对象
3. 复现问题
使用以下JSON数组数据:
[
{
"firstName":"Abderrahim",
"lastName":"Azhrioun"
},
{
"firstName":"Nicole",
"lastName":"Smith"
}
]
当尝试直接反序列化为HashMap
时:
@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`)"));
}
❌ 报错原因:
ObjectMapper
默认将[ ]
解析为List
- 但代码强制要求映射为
HashMap
- 类型不匹配导致反序列化失败
4. 解决方案
方案一:使用List
✅ 最简单粗暴的解决方案:将目标类型改为List<Map<String, String>>
@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);
}
关键点:
- 使用
TypeReference
指定泛型类型 - 每个JSON对象自动转为
Map<String, String>
方案二:自定义POJO类
✅ 更优雅的方案:创建领域对象替代Map
public class Person {
private String firstName;
private String lastName;
// 构造器、getter/setter省略
}
反序列化为List<Person>
:
@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);
}
优势对比: | 方案 | 适用场景 | 代码可读性 | 类型安全 | |------|----------|------------|----------| | List
5. 总结
处理JsonMappingException
的核心要点:
类型匹配原则:
- JSON数组 → 对应Java的
List
或数组 - JSON对象 → 对应Java的
Map
或POJO
- JSON数组 → 对应Java的
解决方案优先级:
- ✅ 优先选择POJO方案(类型安全)
- ⚠️ 仅在临时场景使用
List<Map>
常见踩坑点:
- 错误将
List
强制转为Map
- 忽略
TypeReference
的泛型类型擦除问题
- 错误将
完整示例代码请查阅GitHub仓库