Как использовать Джексона для десериализации массива объектов

781

Документация по привязке данных Джексона указывает, что Джексон поддерживает десериализацию «массивов всех поддерживаемых типов», но я не могу понять точный синтаксис для этого.

Для одного объекта я бы сделал это:

//json input
{
    "id" : "junk",
    "stuff" : "things"
}

//Java
MyClass instance = objectMapper.readValue(json, MyClass.class);

Теперь для массива я хочу сделать это:

//json input
[{
    "id" : "junk",
    "stuff" : "things"
},
{
    "id" : "spam",
    "stuff" : "eggs"
}]

//Java
List<MyClass> entries = ?

Кто-нибудь знает, есть ли волшебная пропущенная команда? Если нет, то каково решение?

Олли Эдвардс
источник
2
Я предпочитаю библиотеку Google GSON для работы с JSON. Стоит проверить, если вы еще не пробовали ... делает работу с ней очень простой и интуитивно понятной.
Джесси Уэбб
11
FWIW Возможные решения этой конкретной проблемы с Gson почти идентичны возможным с помощью API привязки данных Джексона.
Программист Брюс
17
Gweebz - может быть, вы хотели бы объяснить, почему вы чувствуете, что GSON - лучший выбор (по сравнению с Джексоном)?
StaxMan

Ответы:

1660

Сначала создайте маппер:

import com.fasterxml.jackson.databind.ObjectMapper;// in play 2.3
ObjectMapper mapper = new ObjectMapper();

Как массив:

MyClass[] myObjects = mapper.readValue(json, MyClass[].class);

Как список:

List<MyClass> myObjects = mapper.readValue(jsonInput, new TypeReference<List<MyClass>>(){});

Другой способ указать тип списка:

List<MyClass> myObjects = mapper.readValue(jsonInput, mapper.getTypeFactory().constructCollectionType(List.class, MyClass.class));
Программист Брюс
источник
43
Еще одно замечание: если при разборе вы получаете сообщение об ошибке, например, JsonMappingException: No suitable constructor found for typeэто означает, что вам нужно добавить конструктор по умолчанию в ваш класс, добавив частный конструктор без аргументов, исправивший это для меня.
SyntaxRules
12
@SyntaxRules добавление явного конструктора необходимо, если у вас есть явный конструктор - если нет, компилятор автоматически создает открытый «пустой» конструктор. Хорошая точка зрения. Другая распространенная проблема заключается в том, что внутренние классы должны быть static- иначе они никогда не имеют конструктора с нулевым аргументом.
StaxMan
245
Кстати, List<MyClass> myObjects = Arrays.asList(mapper.readValue(json, MyClass[].class))работает в 10 раз быстрее, чем TypeRefence.
5
Я ищу версию универсального типа.
Стефан
1
@EugeneTskhovrebov ваш путь самый чистый для встраивания. Другие требуют кастинга или декларирования. Я предлагаю добавить его в качестве собственного ответа, чтобы помочь выделить его и дать вам несколько повторений.
Эрик Б
190

От Евгения Цховребова

List<MyClass> myObjects = Arrays.asList(mapper.readValue(json, MyClass[].class))

Это решение кажется лучшим для меня

Marthym
источник
Для тех, кто работает с агентами в Java, Lotus Domino, это путь. Я пробовал некоторые другие решения, но всегда получалResourceNotFoundException
John
Для этого решения может потребоваться добавление SyntaxRules в комментариях к ответу выше, как и мы, именно для меня. Я просто хотел добавить это, чтобы оно не потерялось.
Роб
2
илиArrays.asList(Json.fromJson(json.get("fieldName"), MyClass[].class))
Аль-Мотафар
3
илиList<MyClass> myObjects = Arrays.asList(mapper.treeToValue(jsonNode.get("fieldName"), MyClass[].class))
Коллин Кроулл
@CollinKrawll, что делает objectmapper.treetovalue?
Ишвар
35

Для общей реализации:

public static <T> List<T> parseJsonArray(String json,
                                         Class<T> classOnWhichArrayIsDefined) 
                                         throws IOException, ClassNotFoundException {
   ObjectMapper mapper = new ObjectMapper();
   Class<T[]> arrayClass = (Class<T[]>) Class.forName("[L" + classOnWhichArrayIsDefined.getName() + ";");
   T[] objects = mapper.readValue(json, arrayClass);
   return Arrays.asList(objects);
}
Оби Ван - Паллавья
источник
4
Хорошая конструкция класса <T []>. Никогда этого не видел. Где вы нашли информацию об этом?
Руланд Ван Хеддегем
11

Сначала создайте экземпляр ObjectReader, который является потокобезопасным.

ObjectMapper objectMapper = new ObjectMapper();
ObjectReader objectReader = objectMapper.reader().forType(new TypeReference<List<MyClass>>(){});

Тогда используйте это:

List<MyClass> result = objectReader.readValue(inputStream);
Грег Д
источник
Это именно то, что я ищу, спасибо
одинокий
мы получаем - com.fasterxml.jackson.databind.JsonMappingException: не можем десериализовать экземпляр java.util.ArrayList из токена START_OBJECT в [Source: java.io.FileInputStream@33fec21; строка: 1, столбец: 1]
Динеш Кумар
8
try {
    ObjectMapper mapper = new ObjectMapper();
    JsonFactory f = new JsonFactory();
    List<User> lstUser = null;
    JsonParser jp = f.createJsonParser(new File("C:\\maven\\user.json"));
    TypeReference<List<User>> tRef = new TypeReference<List<User>>() {};
    lstUser = mapper.readValue(jp, tRef);
    for (User user : lstUser) {
        System.out.println(user.toString());
    }

} catch (JsonGenerationException e) {
    e.printStackTrace();
} catch (JsonMappingException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}
Джером
источник
3

Вот утилита, которая должна преобразовать json2object или Object2json, независимо от того, что вы используете (объект T)

import java.io.IOException;
import java.io.StringWriter;
import java.util.List;

import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

/**
 * 
 * @author TIAGO.MEDICI
 * 
 */
public class JsonUtils {

    public static boolean isJSONValid(String jsonInString) {
        try {
            final ObjectMapper mapper = new ObjectMapper();
            mapper.readTree(jsonInString);
            return true;
        } catch (IOException e) {
            return false;
        }
    }

    public static String serializeAsJsonString(Object object) throws JsonGenerationException, JsonMappingException, IOException {
        ObjectMapper objMapper = new ObjectMapper();
        objMapper.enable(SerializationFeature.INDENT_OUTPUT);
        objMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
        StringWriter sw = new StringWriter();
        objMapper.writeValue(sw, object);
        return sw.toString();
    }

    public static String serializeAsJsonString(Object object, boolean indent) throws JsonGenerationException, JsonMappingException, IOException {
        ObjectMapper objMapper = new ObjectMapper();
        if (indent == true) {
            objMapper.enable(SerializationFeature.INDENT_OUTPUT);
            objMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
        }

        StringWriter stringWriter = new StringWriter();
        objMapper.writeValue(stringWriter, object);
        return stringWriter.toString();
    }

    public static <T> T jsonStringToObject(String content, Class<T> clazz) throws JsonParseException, JsonMappingException, IOException {
        T obj = null;
        ObjectMapper objMapper = new ObjectMapper();
        obj = objMapper.readValue(content, clazz);
        return obj;
    }

    @SuppressWarnings("rawtypes")
    public static <T> T jsonStringToObjectArray(String content) throws JsonParseException, JsonMappingException, IOException {
        T obj = null;
        ObjectMapper mapper = new ObjectMapper();
        obj = mapper.readValue(content, new TypeReference<List>() {
        });
        return obj;
    }

    public static <T> T jsonStringToObjectArray(String content, Class<T> clazz) throws JsonParseException, JsonMappingException, IOException {
        T obj = null;
        ObjectMapper mapper = new ObjectMapper();
        mapper = new ObjectMapper().configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
        obj = mapper.readValue(content, mapper.getTypeFactory().constructCollectionType(List.class, clazz));
        return obj;
    }
Тиаго Медичи
источник
0

Вы также можете создать класс, который расширяет ArrayList:

public static class MyList extends ArrayList<Myclass> {}

и затем используйте это как:

List<MyClass> list = objectMapper.readValue(json, MyList.class);
pero_hero
источник
0

Я не смог воспользоваться этим ответом, потому что мой линтер не допустил неконтролируемых приведений.

Вот альтернатива, которую вы можете использовать. Я чувствую, что это действительно более чистое решение.

public <T> List<T> parseJsonArray(String json, Class<T> clazz) throws JsonProcessingException {
  var tree = objectMapper.readTree(json);
  var list = new ArrayList<T>();
  for (JsonNode jsonNode : tree) {
    list.add(objectMapper.treeToValue(jsonNode, clazz));
  }
  return list;
}
lbenedetto
источник