Resolving ClassCastException: LinkedHashMap Cannot Be Cast to Custom Objects in Jackson Deserialization

Nov 21, 2025 · Programming · 29 views · 7.8

Keywords: Jackson | Deserialization | ClassCastException | LinkedHashMap | TypeReference | REST Assured

Abstract: This article provides an in-depth analysis of the ClassCastException encountered during JSON deserialization using Jackson, explaining why LinkedHashMap serves as the default deserialization container and offering multiple solutions. Through comparative examples using REST Assured framework and ObjectMapper, it demonstrates how to correctly specify generic type information to avoid type conversion errors. The article also discusses the applicability of TypeReference and CollectionType in different scenarios, providing practical guidance for handling complex JSON data structures.

Problem Background and Exception Analysis

In Java development, when using the Jackson library for JSON deserialization, developers often encounter exceptions like java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.testing.models.Account. The root cause of this issue lies in Jackson's default behavior of using LinkedHashMap as the deserialization container when sufficient type information is lacking.

Jackson Deserialization Mechanism Explained

Jackson, as a popular JSON processing library, relies on type information during the deserialization process. When using methods like as(ArrayList.class), due to type erasure in Java generics, Jackson cannot retrieve the actual type information of elements within the ArrayList. In such cases, Jackson adopts a conservative approach, deserializing JSON arrays into ArrayList<LinkedHashMap> instead of the expected ArrayList<Account>.

The following code example illustrates a typical scenario where this problem occurs:

ArrayList<Account> account = given().when().expect().statusCode(expectedResponseCode)
    .get("accounts/" + newTest.id() + "/users")
    .as(ArrayList.class);
assertThat(account.get(0).getId()).isEqualTo(expectedId);

In this code, the as(ArrayList.class) call loses generic type information, preventing Jackson from correctly recognizing that JSON objects should be deserialized as Account type.

Solution: Using ObjectMapper with TypeReference

The most effective solution involves using Jackson's ObjectMapper in combination with TypeReference to preserve complete type information. This approach bypasses Java's type erasure problem through the type token pattern.

The improved code implementation is as follows:

ObjectMapper mapper = new ObjectMapper();

JsonNode accounts = given().when().expect().statusCode(expectedResponseCode)
    .get("accounts/" + newClub.getOwner().getCustId() + "/clubs")
    .as(JsonNode.class);

List<Account> accountList = mapper.convertValue(
    accounts, 
    new TypeReference<List<Account>>(){}
);

assertThat(accountList.get(0).getId()).isEqualTo(expectedId);

The key advantages of this method include:

Alternative Approach: Using CollectionType

In addition to TypeReference, Jackson's CollectionType can be used to explicitly specify collection types. This approach may be more intuitive and flexible in certain scenarios.

Example implementation:

public <T> List<T> jsonArrayToObjectList(String json, Class<T> tClass) throws IOException {
    ObjectMapper mapper = new ObjectMapper();
    CollectionType listType = mapper.getTypeFactory().constructCollectionType(ArrayList.class, tClass);
    List<T> ts = mapper.readValue(json, listType);
    return ts;
}

Advantages of this approach:

Limitations of REST Assured Framework

When using the REST Assured framework, it's important to recognize that its built-in JSON processing mechanisms may not adequately handle complex type conversion requirements. The framework's as() method has limitations when type information is incomplete, which is why falling back to native Jackson processing becomes necessary.

The case study from the reference article further confirms this issue: when dealing with complex JSON structures containing nested objects, Jackson recursively uses LinkedHashMap as the default container without explicit type guidance.

Best Practice Recommendations

Based on the above analysis, the following best practices are recommended for JSON deserialization:

Conclusion

The core issue behind ClassCastException: LinkedHashMap cannot be cast to custom object problems is the lack of type information. By understanding Jackson's deserialization mechanism and adopting appropriate type specification strategies, developers can effectively avoid such issues. In practical development, the choice between TypeReference and CollectionType depends on specific application scenarios and personal preferences, but the key lies in ensuring the completeness and accuracy of type information.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.