1. 概述

本教程将深入探讨如何解决Jackson异常:JsonMappingException: Can not deserialize instance of java.util.HashMap out of START_ARRAY token。我们将:

  1. 分析异常的根本原因
  2. 通过实际代码示例复现问题
  3. 提供两种解决方案

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 | 快速原型 | ⭐⭐ | ❌ | | 自定义POJO | 生产环境 | ⭐⭐⭐⭐⭐ | ✅ |

5. 总结

处理JsonMappingException的核心要点:

  1. 类型匹配原则

    • JSON数组 → 对应Java的List或数组
    • JSON对象 → 对应Java的Map或POJO
  2. 解决方案优先级

    • ✅ 优先选择POJO方案(类型安全)
    • ⚠️ 仅在临时场景使用List<Map>
  3. 常见踩坑点

    • 错误将List强制转为Map
    • 忽略TypeReference的泛型类型擦除问题

完整示例代码请查阅GitHub仓库


原始标题:Fix the JsonMappingException: Can not deserialize instance of java.util.HashMap out of START_ARRAY token | Baeldung