Can not deserialize instance of java.util.ArrayList out of START_OBJECT token
Categories:
Resolving 'Cannot deserialize instance of java.util.ArrayList out of START_OBJECT token'

Understand and fix the common Jackson deserialization error when expecting a list but receiving a single object in Java applications using Spring, JAX-RS, or RESTEasy.
The error message Cannot deserialize instance of java.util.ArrayList out of START_OBJECT token
is a common issue encountered when working with JSON deserialization in Java, particularly with libraries like Jackson (used by Spring, JAX-RS, and RESTEasy). This error typically occurs when your application expects to receive a JSON array (which maps to a java.util.ArrayList
or similar collection type) but instead receives a single JSON object. This article will explain why this happens and provide practical solutions.
Understanding the Deserialization Mismatch
JSON data can represent collections in two primary ways: as an array of objects or as a single object. When your Java code is set up to deserialize a JSON array into an ArrayList
, but the incoming JSON payload is a single object, Jackson throws this error because it cannot convert a START_OBJECT
token (which signifies the beginning of a JSON object {}
) into a java.util.ArrayList
(which expects a START_ARRAY
token []
).
flowchart TD A[Client Sends JSON] --> B{Server Expects Array?} B -- Yes --> C{Received JSON Array?} C -- Yes --> D[Deserialization Success] C -- No --> E[Received JSON Object?] E -- Yes --> F["Error: Cannot deserialize ArrayList out of START_OBJECT"] E -- No --> G[Other JSON Format?] B -- No --> H[Server Expects Object?] H -- Yes --> I{Received JSON Object?} I -- Yes --> J[Deserialization Success] I -- No --> K[Received JSON Array?] K -- Yes --> L["Error: Cannot deserialize Object out of START_ARRAY"] K -- No --> G
Flowchart illustrating JSON deserialization mismatch scenarios.
This often happens due to inconsistencies between the client sending the data and the server expecting it, or changes in API specifications that are not reflected in the code. For example, an API might return {"id": 1, "name": "Item A"}
for a single item, but [{"id": 1, "name": "Item A"}, {"id": 2, "name": "Item B"}]
for multiple items. If your code always expects the latter, the former will cause this error.
Common Causes and Solutions
Let's explore the most frequent reasons for this error and how to address them.
1. Mismatched API Response Structure
The most common cause is that the API you're consuming sometimes returns a single object and sometimes an array, depending on the number of results. Your Java code, however, is strictly defined to expect an ArrayList
.
public class MyController {
@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response processItems(List<MyObject> items) { // Expects an array
// ... logic
return Response.ok().build();
}
}
// Client sends:
// {"id": 1, "name": "Item A"} <-- ERROR
// Client sends:
// [{"id": 1, "name": "Item A"}, {"id": 2, "name": "Item B"}] <-- OK
Example of a JAX-RS endpoint expecting a List.
Solution 1.1: Adjusting the Expected Type
If the API can legitimately return a single object, and you want to treat it as a list containing one element, you can configure Jackson to accept a single value as an array. This is often done using DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY
.
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.List;
public class JacksonConfig {
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
return mapper;
}
}
// In Spring Boot, you can configure this in application.properties/yml or a @Configuration class:
// spring.jackson.deserialization.accept-single-value-as-array=true
Configuring Jackson to accept single values as arrays.
ACCEPT_SINGLE_VALUE_AS_ARRAY
can sometimes mask underlying API inconsistencies. Use it judiciously and ensure it aligns with your data contract expectations.Solution 1.2: Creating a Wrapper Object
A more robust solution, especially if the API structure is inconsistent, is to define a wrapper object that can handle both scenarios. This involves creating a custom deserializer or a flexible DTO (Data Transfer Object).
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class MyObjectWrapper {
private List<MyObject> items;
@JsonCreator
public MyObjectWrapper(List<MyObject> items) {
this.items = items;
}
@JsonCreator
public MyObjectWrapper(MyObject item) {
this.items = Collections.singletonList(item);
}
@JsonValue
public List<MyObject> getItems() {
return items;
}
}
// Then, in your controller:
// public Response processItems(MyObjectWrapper wrapper) { ... }
Using a wrapper object with @JsonCreator
to handle both single object and array inputs.
2. Incorrect Endpoint or Request Body
Sometimes, the issue isn't with the deserialization logic itself, but with what's being sent. You might be sending a single object to an endpoint designed for a collection, or vice-versa.
public class MyController {
// Endpoint 1: For a single item
@POST
@Path("/item")
public Response createItem(MyObject item) { ... }
// Endpoint 2: For multiple items
@POST
@Path("/items")
public Response createItems(List<MyObject> items) { ... }
}
// If you send {"id": 1, "name": "A"} to /items, you'll get the error.
// It should be sent to /item.
Two distinct endpoints for single and multiple items.
Solution 2.1: Verify Client Request
Ensure the client (e.g., frontend application, another microservice) is sending the correct JSON structure to the correct endpoint. If the server expects List<MyObject>
, the client must send a JSON array []
.
// Correct JSON for List<MyObject>
[
{
"id": 1,
"name": "Item A"
},
{
"id": 2,
"name": "Item B"
}
]
Example of a correct JSON array payload.
// Incorrect JSON that causes the error if List<MyObject> is expected
{
"id": 1,
"name": "Item A"
}
Example of an incorrect JSON object payload when an array is expected.
3. Using TypeReference
for Generic Types
When deserializing generic types like List<MyObject>
outside of a method parameter (e.g., from a file or a string), you need to use TypeReference
to provide Jackson with the full generic type information.
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.util.List;
public class DeserializationExample {
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
String jsonArray = "[{\"id\":1,\"name\":\"Item A\"},{\"id\":2,\"name\":\"Item B\"}]";
String jsonObject = "{\"id\":1,\"name\":\"Item A\"}";
// Correct way to deserialize a List<MyObject>
List<MyObject> items = mapper.readValue(jsonArray, new TypeReference<List<MyObject>>() {});
System.out.println("Deserialized array: " + items);
// This would cause the error if ACCEPT_SINGLE_VALUE_AS_ARRAY is not enabled:
// List<MyObject> singleItem = mapper.readValue(jsonObject, new TypeReference<List<MyObject>>() {});
// System.out.println("Deserialized single item as list: " + singleItem);
}
}
class MyObject {
public int id;
public String name;
// Getters, setters, and toString()
@Override
public String toString() {
return "MyObject{id=" + id + ", name='" + name + '\'' + '}';
}
}
Using TypeReference
for correct generic type deserialization.
Conclusion
The Cannot deserialize instance of java.util.ArrayList out of START_OBJECT token
error is a clear indicator of a mismatch between the expected Java type and the actual JSON structure. By carefully examining the incoming JSON payload and applying the appropriate solutions—whether it's configuring Jackson to be more lenient, creating robust wrapper objects, or ensuring client-server contract alignment—you can effectively resolve this common deserialization problem in your Java applications.