Jackson: как десериализовать JSON-массив объектов в Java
[
{"name": "Alice", "age": 30},
{"name": "Bob", "age": 25},
{"name": "Charlie", "age": 35}
]
Я пытаюсь прочитать этот JSON и превратить его в список объектов моего класса `Person`. Мой класс выглядит так:
public class Person {
private String name;
private int age;
// Конструктор по умолчанию
public Person() {}
// Геттеры и сеттеры
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
}
Я пробую такой код:
ObjectMapper mapper = new ObjectMapper();
String json = "[{\"name\":\"Alice\",\"age\":30},{\"name\":\"Bob\",\"age\":25}]";
Person person = mapper.readValue(json, Person.class);
Но получаю исключение: `com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type 'Person' from Array value (token 'JsonToken.START_ARRAY')`. Я понимаю, что Jackson ожидает один объект, а получает массив. Как правильно десериализовать массив объектов? Подскажите, пожалуйста, как это сделать. Заранее спасибо!
<<Проблема и её решение
Вы столкнулись с типичной ситуацией: Jackson ожидает один JSON-объект (например, {"name":"Alice","age":30}), а вы передаёте массив объектов. Чтобы десериализовать массив, нужно явно указать тип коллекции. Вот несколько способов.
Способ 1: Использование TypeReference (рекомендуется)
Самый гибкий и безопасный способ — использовать TypeReference. Он позволяет Jackson корректно определить generic-тип:
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.List;
ObjectMapper mapper = new ObjectMapper();
String json = "[{\"name\":\"Alice\",\"age\":30},{\"name\":\"Bob\",\"age\":25}]";
List<Person> people = mapper.readValue(json, new TypeReference<List<Person>>() {});
System.out.println(people); // [Person{name='Alice', age=30}, Person{name='Bob', age=25}]
Почему это работает? TypeReference сохраняет информацию о generic-типе во время выполнения, что необходимо из-за стирания типов (type erasure) в Java. Без него Jackson не знает, что внутри List находятся объекты Person.
Способ 2: Использование массива классов
Можно десериализовать в массив Person[], а затем преобразовать в список:
Person[] peopleArray = mapper.readValue(json, Person[].class);
List<Person> people = Arrays.asList(peopleArray);
Этот способ проще, но возвращает массив фиксированной длины. Если вам нужен изменяемый список, оберните результат в new ArrayList<>(Arrays.asList(...)).
Способ 3: Использование CollectionType (для продвинутых)
Можно использовать фабрику типов Jackson:
JavaType type = mapper.getTypeFactory().constructCollectionType(List.class, Person.class);
List<Person> people = mapper.readValue(json, type);
Этот подход даёт полный контроль и полезен, когда тип коллекции динамический.
Важные моменты
- Конструктор по умолчанию: Убедитесь, что у класса
Personесть конструктор без параметров (или используйте аннотацию@JsonCreator). - Геттеры/сеттеры: Jackson использует их для заполнения полей. Если их нет, добавьте аннотации
@JsonPropertyили настройте видимость черезObjectMapper. - Обработка ошибок: Всегда оборачивайте вызов в try-catch или пробрасывайте исключение.
Полный рабочий пример
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.List;
public class Main {
public static void main(String[] args) throws Exception {
String json = "[{\"name\":\"Alice\",\"age\":30},{\"name\":\"Bob\",\"age\":25}]";
ObjectMapper mapper = new ObjectMapper();
List<Person> people = mapper.readValue(json, new TypeReference<List<Person>>() {});
people.forEach(p -> System.out.println(p.getName() + " - " + p.getAge()));
}
}
Вывод: Alice - 30 и Bob - 25.
Используйте TypeReference как основной паттерн — это чисто, безопасно и работает с любыми вложенными generic-типами (например, Map<String, List<Person>>).
Альтернативный подход: десериализация в массив с последующей конвертацией
Если вы не хотите использовать TypeReference или работать с коллекциями напрямую, можно десериализовать JSON в массив объектов, а затем преобразовать его в список. Это может быть удобно, когда вам нужно быстро протестировать код или вы работаете с фиксированным количеством элементов.
ObjectMapper mapper = new ObjectMapper();
String json = "[{\"name\":\"Alice\",\"age\":30},{\"name\":\"Bob\",\"age\":25}]";
Person[] peopleArray = mapper.readValue(json, Person[].class);
// Если нужен список:
List<Person> peopleList = new ArrayList<>(Arrays.asList(peopleArray));
Плюсы:
- Простота и читаемость кода.
- Не требует импорта
TypeReference.
Минусы:
- Массив фиксированной длины (если нужен изменяемый список, требуется дополнительная обёртка).
- Менее гибкий для сложных вложенных структур.
Этот способ хорошо подходит для простых случаев, когда вы точно знаете, что JSON — это плоский массив объектов. Для более сложных сценариев (например, вложенные generic-типы) лучше использовать TypeReference.