1. 概述

在这个简短的教程中,我们将讲解如何解决“JsonMappingException: 无法将Object值反序列化为java.util.ArrayList实例”这个异常。

首先,我们会揭示异常的主要原因,然后在实践中演示如何重现它,最后介绍如何解决这个问题。

2. 理解异常

通常情况下,当Jackson在解析JSON字符串时遇到致命映射错误时,会抛出JsonMappingException。因此,堆栈跟踪中的“无法将...序列化为ArrayList实例”表明Jackson未能将JSON属性映射到一个ArrayList对象。

简而言之,这种异常最常见的原因是使用花括号“{…}”来表示集合,而不是方括号“[…]”。

3. 重现异常

既然我们知道Jackson抛出异常的原因,让我们通过一个实际例子来演示如何重现它。

考虑一下Country类:

public class Country {

    private String name;
    private List<String> cities;

    // standard getters and setters
}

如图所示,一个国家由一个名称和一系列城市定义。

接下来,假设我们在JSON字符串中使用花括号来定义城市:

{
    "name": "Netherlands", 
    "cities": {"Amsterdam", "Tamassint"}
}

现在尝试将JSON字符串反序列化为Country类型的对象,会得到以下结果:

Cannot deserialize value of type `java.util.ArrayList<java.lang.String>` from Object value (token `JsonToken.START_OBJECT`)
at [Source: (String)"{"name":"Netherlands","cities":{"Amsterdam", "Tamassint"}}"; line: 1, column: 32] 
(through reference chain: com.baeldung.mappingexception.Country["cities"])
...

最后,我们创建一个测试用例来确认这一点:

@Test
public final void givenJsonWithInvalidList_whenDeserializing_thenThrowException() throws JsonParseException, IOException {
    String json = "{\"name\":\"Netherlands\",\"cities\":{\"Amsterdam\", \"Tamassint\"}}";
    ObjectMapper mapper = new ObjectMapper();

    Exception exception = assertThrows(JsonMappingException.class, () -> mapper.reader()
      .forType(Country.class)
      .readValue(json));

    assertTrue(exception.getMessage()
      .contains("Cannot deserialize value of type `java.util.ArrayList<java.lang.String>`"));
}

如上所述,Jackson因“无法将java.util.ArrayList<java.lang.String>类型反序列化”而失败。

主要原因在于我们使用了花括号来表示城市列表。对于Jackson来说,{“Amsterdam”,“Tamassint”}不是一个JSON数组。

4. 解决异常

避免这种异常的最简单方法是使用方括号而非花括号来定义元素集合。 因此,要解决异常,我们需要先修复JSON字符串:

{ 
    "name": "Netherlands",
    "cities": ["Amsterdam", "Tamassint"]
}

现在,让我们验证是否一切按预期工作,使用测试用例:

@Test
public final void givenJsonWithValidList_whenDeserializing_thenCorrect() throws JsonParseException, IOException {
    String json = "{\"name\":\"Netherlands\",\"cities\":[\"Amsterdam\", \"Tamassint\"]}";
    ObjectMapper mapper = new ObjectMapper();

    Country country = mapper.reader()
      .forType(Country.class)
      .readValue(json);

    assertEquals("Netherlands", country.getName());
    assertEquals(Arrays.asList("Amsterdam", "Tamassint"), country.getCities());
}

正如我们所见,新的JSON字符串成功地反序列化为一个Country对象。

5. 总结

在这篇简短的文章中,我们讨论了“*JsonMappingException: 无法将Object值反序列化为java.util.ArrayList实例 (token JsonToken.START_OBJECT)*”异常的主要原因。我们展示了如何产生这个异常,并给出了解决方案。

如往常一样,所有示例的完整源代码可在GitHub上找到:https://github.com/eugenp/tutorials/tree/master/jackson-modules/jackson-exceptions