Receiving JSON and Deserializing as List of Objects in Spring MVC Controller

Dec 07, 2025 · Programming · 11 views · 7.8

Keywords: Spring MVC | JSON Deserialization | Wrapper Class

Abstract: This article addresses the ClassCastException issue when handling JSON array requests in Spring MVC controllers. By analyzing the impact of Java type erasure on Jackson deserialization, it proposes using wrapper classes as a solution and compares alternative methods like custom list types and array parameters. The article explains the error cause in detail, provides code examples, and discusses best practices to help developers efficiently process complex JSON data.

In Spring MVC applications, handling JSON requests is a common requirement, but using List<Object> directly as a controller method parameter can lead to a java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.example.Entity error. Based on actual Q&A data, this article delves into this issue and provides effective solutions.

Problem Background and Error Analysis

A developer attempts to receive a JSON array and deserialize it into a List<TestS> object list in a Spring MVC controller. The controller method signature is as follows:

@RequestMapping(value="/setTest", method=RequestMethod.POST, consumes="application/json")
public @ResponseBody ModelMap setTest(@RequestBody List<TestS> refunds, ModelMap map) {
    for(TestS r : refunds) {
        System.out.println(r.getName());
    }
    // other codes
}

The corresponding POJO class TestS includes name and age fields with their getter/setter methods. The front-end sends a JSON request via jQuery:

var z = '[{"name":"1","age":"2"},{"name":"1","age":"3"}]';
$.ajax({
    url: "/setTest",
    data: z,
    type: "POST",
    dataType:"json",
    contentType:'application/json'               
});

However, a ClassCastException is thrown at runtime, indicating that LinkedHashMap cannot be cast to TestS. The root cause is Java's type erasure mechanism: after compilation, generic information <TestS> is removed, and Jackson cannot recognize the specific type of list elements during deserialization, defaulting to LinkedHashMap for JSON objects.

Solution: Using a Wrapper Class

The best practice is to create a wrapper class to explicitly specify the list type. For example, define a PersonWrapper class:

public class PersonWrapper {
    private List<Person> persons;
    
    public List<Person> getPersons() {
        return persons;
    }
    
    public void setPersons(List<Person> persons) {
        this.persons = persons;
    }
}

Here, the Person class corresponds to TestS, containing name and age fields. The controller method is modified to accept a PersonWrapper parameter:

@RequestMapping(value="person", method=RequestMethod.POST, consumes="application/json", produces="application/json")
@ResponseBody
public List<String> savePerson(@RequestBody PersonWrapper wrapper) {
    List<String> response = new ArrayList<String>();
    for (Person person: wrapper.getPersons()) {
        personService.save(person);
        response.add("Saved person: " + person.toString());
    }
    return response;
}

The front-end JSON request is adjusted to include a wrapper key:

{"persons":[{"name":"shail1","age":"2"},{"name":"shail2","age":"3"}]}

This approach preserves generic information through the wrapper class, enabling Jackson to correctly deserialize into a Person object list and avoid type casting errors.

Comparison of Alternative Approaches

Besides wrapper classes, other answers propose alternative methods:

The wrapper class solution scores highest (10.0) because it clearly separates concerns, is easy to extend, and aligns with object-oriented design principles. The custom list type scores 2.9, and the array parameter scores 2.5, indicating they are usable in specific scenarios but less general.

Implementation Details and Considerations

During implementation, ensure Spring is correctly configured to support Jackson. For Spring 3.1.2 and Jackson 2.0.4, add the following dependency and configuration:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.0.4</version>
</dependency>

And enable annotation-driven support in Spring MVC configuration:

<mvc:annotation-driven />

The wrapper class should provide a no-argument constructor and standard getters/setters to be compatible with Jackson's default deserialization mechanism. For complex nested structures, combine with annotations like @JsonCreator to customize deserialization logic.

Conclusion and Best Practices

When handling JSON array deserialization, using a wrapper class is recommended to circumvent type erasure issues. This enhances code readability and maintainability, facilitating future data structure changes. In practice, choose the appropriate solution based on project needs, but wrapper classes are optimal for most scenarios. By deeply understanding the interaction between Jackson and Spring MVC, developers can build more robust web applications efficiently.

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.