Can not deserialize instance of java.util.ArrayList out of START_OBJECT token

Learn can not deserialize instance of java.util.arraylist out of start_object token with practical examples, diagrams, and best practices. Covers java, spring, jackson development techniques with v...

Resolving 'Cannot deserialize instance of java.util.ArrayList out of START_OBJECT token'

Hero image for Can not 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.

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.