Post

Java JsonMappingException - Can not deserialize instance of java.util.ArrayList from Object value (token JsonToken.START_OBJECT)

1. Exception 이해

일반적으로 Jackson은 JSON 문자열을 역직렬화할 때 치명적인 매핑 오류를 알리기 위해 JsonMappingException을 발생시킨다. 따라서 stack trace “Can not deserialize instance of java.util.ArrayList”는 Jackson이 JSON 속성을 ArrayList 인스턴스에 매핑하지 못했음을 나타낸다.

간단히 말해서, 이 exception의 가장 일반적인 원인은 대괄호 [...] 대신 컬렉션을 나타내기 위해 중괄호 {...}를 사용하는 것이다.

2. Exception 재현

Jackson이 JsonMappingException을 발생시키는 원인을 알았으므로 실제 예를 사용하여 이를 재현하는 방법이다.

Country 클래스를 생성한다.

1
2
3
4
5
6
7
public class Country {

    private String name;
    private List<String> cities;

    // standard getters and setters
}

보시다시피 국가는 이름과 도시 목록으로 정의된다.

다음으로 중괄호를 사용하여 JSON 문자열에서 도시를 정의한다고 가정한다.

1
2
3
4
{
    "name": "Netherlands", 
    "cities": {"Amsterdam", "Tamassint"}
}

이제 JSON 문자열을 국가 유형의 개체로 역직렬화하려고 하면 다음과 같은 결과가 발생한다.

1
2
3
4
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"])
...

마지막으로 이를 확인하기 위해 테스트 사례를 만든다.

1
2
3
4
5
6
7
8
9
10
11
12
@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은 Cannot deserialize value of type 'java.util.ArrayList<java.lang.String>'라는 메시지와 함께 실패한다.

여기서 주된 이유는 도시 목록을 나타내기 위해 중괄호를 사용했기 때문이다. Jackson의 경우 {"Amsterdam", "Tamassint"}는 JSON 배열이 아니다.

3. Exception 수정

예외를 피하는 가장 쉬운 방법은 중괄호 대신 대괄호를 사용하여 요소 컬렉션을 정의하는 것이다. 따라서 예외를 해결하려면 먼저 JSON 문자열을 수정해야 한다.

1
2
3
4
{ 
    "name": "Netherlands",
    "cities": ["Amsterdam", "Tamassint"]
}

이제 테스트를 사용하여 모든 것이 예외적으로 작동하는지 확인한다.

1
2
3
4
5
6
7
8
9
10
11
12
@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 개체로 성공적으로 역직렬화되었다.

[출처 및 참고]

This post is licensed under CC BY 4.0 by the author.