Convert JSON Array to List in Java: Jackson, Gson, Org.json

Convert JSON array to list in Java with Jackson 3 TypeReference, Gson TypeToken and Org.json, including records, unknown fields and large files.

Jackson reading a JSON array with List.class returns LinkedHashMap elements, while TypeReference returns Book records

To convert a JSON array to a list in Java, we parse the array with a JSON library and tell the library the element type, such as new TypeReference<List<Book>>() {} in Jackson or new TypeToken<List<Book>>() {} in Gson. Without the element type, the library returns a list of maps instead of a list of our objects.

We need the conversion whenever a REST API, a message queue or a file sends a list of items, for example search results, order lines or test data loaded from src/test/resources.

The following example converts a JSON array of books into a List<Book> with Jackson 3.

String json = """
    [{"id":1,"title":"Dune","year":1965},{"id":2,"title":"Emma","year":1815}]""";
JsonMapper mapper = JsonMapper.builder().build();
List<Book> books = mapper.readValue(json, new TypeReference<List<Book>>() {});  // [Book[id=1, title=Dune, year=1965], Book[id=2, title=Emma, year=1815]]
String firstTitle = books.get(0).title();                                     // "Dune"
record Book(long id, String title, int year) {}

Notice the empty braces after TypeReference, which create a small anonymous subclass that keeps the generic type. We cover Jackson, Gson and Org.json in turn, including arrays of strings and maps, unknown fields, large files and the errors each library throws, and compare the three at the end.

1. Setup

The examples read a books.json file from the src/main/resources folder, which Maven and Gradle copy to the root of the classpath. The file holds a JSON array of three books.

[
  {"id": 1, "title": "Dune", "year": 1965},
  {"id": 2, "title": "Emma", "year": 1815},
  {"id": 3, "title": "Ulysses", "year": 1922}
]

Each book maps to the Book record from the intro. Jackson 2.12 and later, Jackson 3 and Gson 2.10 and later create records through their canonical constructor, so we no longer need Lombok or setters for the target class.

We open the file with getResourceAsStream() and pass the InputStream to the library. Building a Path from getResource().toURI() works in the IDE but throws a FileSystemNotFoundException once the app runs from a JAR, which is why reading files from the resources folder goes through an InputStream.

JsonMapper mapper = JsonMapper.builder().build();
List<Book> books;
try (InputStream in = Book.class.getResourceAsStream("/books.json")) {
    books = mapper.readValue(in, new TypeReference<List<Book>>() {});
}
int count = books.size();                                             // 3
String last = books.get(2).title();                                   // "Ulysses"

2. Converting With Jackson 3

Jackson is the most widely used JSON library for Java, and Spring Boot uses it by default, so in a Spring Boot application we inject the existing JsonMapper bean instead of creating one. A mapper is immutable and thread-safe, so one instance serves the whole app.

2.1. Adding Jackson 3 to the Build

Jackson 3, released in October 2025, moved to the tools.jackson group ID and package, requires Java 17 and is the version that Spring Boot 4 uses. Version 3.2.3 is the latest.

<dependency>
  <groupId>tools.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>3.2.3</version>
</dependency>

Projects on Jackson 2 use com.fasterxml.jackson.core:jackson-databind 2.22.3 and import from com.fasterxml.jackson. Jackson 2 runs the same conversion code, but it throws checked exceptions and fails on unknown properties by default, as section 2.3 shows.

2.2. Convert JSON Array to List

Java erases generic types at compile time, so List<Book>.class does not exist and readValue(json, List.class) cannot know the element type. Jackson fills such a list with LinkedHashMap objects, and the error shows up later, at the first line that reads an element as a Book.

Jackson reading a JSON array with List.class returns LinkedHashMap elements, while TypeReference returns Book records
Passing List.class loses the element type; TypeReference keeps it, so Jackson creates Book objects
String json = """
    [{"id":1,"title":"Dune","year":1965},{"id":2,"title":"Emma","year":1815}]""";
JsonMapper mapper = JsonMapper.builder().build();
List<?> untyped = mapper.readValue(json, List.class);                 // [{id=1, title=Dune, year=1965}, {id=2, title=Emma, year=1815}]
String element = untyped.get(0).getClass().getSimpleName();           // "LinkedHashMap"
List<Book> typed = mapper.readValue(json, new TypeReference<List<Book>>() {});  // [Book[id=1, title=Dune, year=1965], Book[id=2, title=Emma, year=1815]]

Jackson offers four ways to pass the element type, and all of them return the same list.

String json = """
    [{"id":1,"title":"Dune","year":1965},{"id":2,"title":"Emma","year":1815}]""";
JsonMapper mapper = JsonMapper.builder().build();
// 1. TypeReference
List<Book> byReference = mapper.readValue(json, new TypeReference<List<Book>>() {});
// 2. readerForListOf()
List<Book> byReader = mapper.readerForListOf(Book.class).readValue(json);
// 3. A Java array, then a list
List<Book> byArray = List.of(mapper.readValue(json, Book[].class));
// 4. A JavaType built at runtime
JavaType listType = mapper.getTypeFactory().constructCollectionType(List.class, Book.class);
List<Book> byJavaType = mapper.readValue(json, listType);
boolean allEqual = byReference.equals(byReader) && byReader.equals(byArray) && byArray.equals(byJavaType);  // true

The TypeReference and readerForListOf() forms suit a type known at compile time. The constructCollectionType() form fits generic code, such as a helper method readList(String json, Class<T> type), where the element class arrives as a parameter. The array form returns an unmodifiable list here because of List.of(), whereas the other forms return an ArrayList.

Arrays of simple values and arrays of objects without a matching class work the same way. A List<Map<String, Object>> is useful when the fields differ from item to item, and the JSON to Map article goes deeper into that case.

JsonMapper mapper = JsonMapper.builder().build();
List<String> tags = mapper.readValue("[\"java\",\"json\"]", new TypeReference<List<String>>() {});  // [java, json]
List<Map<String, Object>> rows = mapper.readValue("[{\"id\":1,\"title\":\"Dune\"}]", new TypeReference<List<Map<String, Object>>>() {});  // [{id=1, title=Dune}]

2.3. Unknown Fields and Invalid JSON

APIs add fields over time, so the JSON often has properties that our record does not declare. Jackson 3 ignores unknown properties by default, while Jackson 2 throws an UnrecognizedPropertyException. To get the strict behavior in Jackson 3, we enable FAIL_ON_UNKNOWN_PROPERTIES on the builder.

String withIsbn = """
    [{"id":1,"title":"Dune","year":1965,"isbn":"978-0441013593"}]""";
List<Book> lenient = JsonMapper.builder().build().readValue(withIsbn, new TypeReference<List<Book>>() {});  // [Book[id=1, title=Dune, year=1965]]
JsonMapper strict = JsonMapper.builder().enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES).build();
List<Book> rejected = strict.readValue(withIsbn, new TypeReference<List<Book>>() {});  // UnrecognizedPropertyException

Broken JSON, such as a missing bracket, throws a StreamReadException. In Jackson 3, every Jackson exception extends the unchecked JacksonException, so we catch that one type at the boundary where the JSON arrives and turn it into a clear error for the caller.

static List<Book> parseBooks(JsonMapper mapper, String json) {
    try {
        return mapper.readValue(json, new TypeReference<List<Book>>() {});
    } catch (JacksonException e) {
        throw new IllegalArgumentException("Invalid book list: " + e.getOriginalMessage(), e);
    }
}
List<Book> broken = parseBooks(JsonMapper.builder().build(), "[{\"id\":1,");  // IllegalArgumentException: Invalid book list

2.4. Reading a Large JSON Array One Element at a Time

A readValue() call builds the whole list in memory. For example, a nightly job imports a 2 GB product export, which is one JSON array with millions of items. Loading that file into a list needs several gigabytes of heap, whereas processing one item at a time needs memory for one item.

A MappingIterator does that. When the root value is an array, readValues() iterates over its elements, and we process each one and let it go.

JsonMapper mapper = JsonMapper.builder().build();
int imported = 0;
try (InputStream in = Book.class.getResourceAsStream("/books.json");
     MappingIterator<Book> iterator = mapper.readerFor(Book.class).readValues(in)) {
    while (iterator.hasNext()) {
        Book book = iterator.next();
        imported++;                       // save the book, then forget it
    }
}
int total = imported;                                                 // 3

Gson has the same option with its JsonReader streaming parser.

3. Using Gson

Gson, developed by Google, is a small library with no dependencies that maps JSON to Java objects through reflection. Its API has fewer options than Jackson’s, which makes it a good fit for Android apps and small tools.

3.1. Gson Dependency

Version 2.14.0 is the latest Gson release.

<dependency>
  <groupId>com.google.code.gson</groupId>
  <artifactId>gson</artifactId>
  <version>2.14.0</version>
</dependency>

3.2. Reading the Array With a TypeToken

Where Jackson uses TypeReference, Gson uses TypeToken to keep the generic type that erasure removes. The static method TypeToken.getParameterized() builds the same type from a Class at runtime. A Gson instance is thread-safe, so we create it once.

String json = """
    [{"id":1,"title":"Dune","year":1965},{"id":2,"title":"Emma","year":1815}]""";
Gson gson = new Gson();
Type listType = new TypeToken<List<Book>>() {}.getType();
List<Book> books = gson.fromJson(json, listType);                     // [Book[id=1, title=Dune, year=1965], Book[id=2, title=Emma, year=1815]]
List<Book> sameBooks = gson.fromJson(json, TypeToken.getParameterized(List.class, Book.class).getType());  // [Book[id=1, title=Dune, year=1965], Book[id=2, title=Emma, year=1815]]

Without a TypeToken, Gson returns LinkedTreeMap elements and reads every number as a Double, so an id of 1 turns into 1.0. Gson ignores unknown fields, and it throws a JsonSyntaxException for broken JSON. More Gson variants, such as arrays of strings, are in parsing JSON arrays with Gson.

Gson gson = new Gson();
List<?> raw = gson.fromJson("[{\"id\":1,\"title\":\"Dune\"}]", List.class);  // [{id=1.0, title=Dune}]
List<Book> broken = gson.fromJson("[{\"id\":1,", new TypeToken<List<Book>>() {}.getType());  // JsonSyntaxException

4. Using Org.json

Org.json, also called JSON-Java, is the reference implementation of the JSON format by Douglas Crockford, and it follows RFC 8259. It does not bind JSON to classes, so we read each field by name and build the objects ourselves, which gives full control at the cost of more code.

4.1. Org.json Dependency

Org.json uses dates as version numbers, and 20260814 is the latest release.

<dependency>
  <groupId>org.json</groupId>
  <artifactId>json</artifactId>
  <version>20260814</version>
</dependency>

4.2. Building the List From a JSONArray

The readBooks() method parses the array into a JSONArray, reads each element as a JSONObject and creates a Book from its fields.

  • JSONArray represents the whole array of books.
  • JSONObject represents one book in the array.
static List<Book> readBooks(String json) {
    JSONArray array = new JSONArray(json);
    List<Book> books = new ArrayList<>();
    for (int i = 0; i < array.length(); i++) {
        JSONObject item = array.getJSONObject(i);
        books.add(new Book(item.getLong("id"), item.optString("title", "Untitled"), item.getInt("year")));
    }
    return books;
}
List<Book> books = readBooks("[{\"id\":1,\"year\":1965}]");          // [Book[id=1, title=Untitled, year=1965]]
List<Book> noYear = readBooks("[{\"id\":1,\"title\":\"Dune\"}]");  // JSONException: JSONObject["year"] not found.
List<Object> generic = new JSONArray("[\"java\",\"json\"]").toList();  // [java, json]

The getXxx() methods throw a JSONException for a missing key, and the optXxx() methods return a default value instead. The toList() method converts the array into plain Java objects, where each JSON object becomes a HashMap, so the key order of the original JSON is lost.

5. Comparing Jackson, Gson and Org.json

All three libraries convert the array correctly, and they differ in how much they do for us.

FeatureJackson 3Gson 2.14Org.json
Binds to classes and recordsYesYesNo, manual field reads
Element type syntaxTypeReference, readerForListOf()TypeTokenNot needed
Unknown fields by defaultIgnoredIgnoredNot applicable
Error typeJacksonException (unchecked)JsonSyntaxException (unchecked)JSONException (unchecked)
Streaming for large arraysMappingIteratorJsonReaderNo
Default in Spring Boot 4YesNoNo

In a Spring Boot application, use the Jackson mapper that is already configured, because it applies the same settings as the REST layer. Gson fits small apps and Android code, and Org.json fits code that reads a few fields from JSON of varying shape without defining classes.

6. JSON Array to List FAQs

The ClassCastException with LinkedHashMap and the exact list type returned are the two points behind most of these questions.

6.1. Why do we get “LinkedHashMap cannot be cast to class Book”?

We passed List.class instead of the element type, so Jackson created maps, as section 2.2 shows. Pass new TypeReference<List<Book>>() {} or use readerForListOf(Book.class), and the elements become Book objects.

6.2. How do we convert a JSON array to an ArrayList?

Jackson and Gson return an ArrayList for a List target, but the type is not part of their contract. To be sure, ask for an ArrayList in the type or copy the result.

JsonMapper mapper = JsonMapper.builder().build();
ArrayList<String> names = mapper.readValue("[\"Dune\",\"Emma\"]", new TypeReference<ArrayList<String>>() {});  // [Dune, Emma]

6.3. How do we convert a JSON array of strings to a List?

Use the same call with String as the element type, such as new TypeReference<List<String>>() {} in Jackson or new TypeToken<List<String>>() {} in Gson. In Org.json, new JSONArray(json).toList() returns the strings as a List<Object>.

6.4. How do we convert a List back to a JSON array?

Call writeValueAsString(list) on the Jackson mapper or toJson(list) on Gson. Both write a JSON array with one object per element, and the Jackson read and write guide covers the writing side in detail.

String written = JsonMapper.builder().build().writeValueAsString(List.of(new Book(1, "Dune", 1965)));  // "[{"id":1,"title":"Dune","year":1965}]"

7. Conclusion

Converting a JSON array to a list takes one call once the library knows the element type. Jackson uses TypeReference, readerForListOf() or a JavaType, Gson uses TypeToken, and Org.json builds the list in a loop over a JSONArray.

Records make the target class a single line, and reading the input with getResourceAsStream() keeps the code working inside a JAR. Jackson 3 ignores unknown properties and throws unchecked exceptions, which changes the error handling for code moving from Jackson 2, and a MappingIterator handles arrays too large for memory. The Java collections tutorial covers what to do with the resulting list.

8. References

Happy Learning !!

Source Code on Github

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.