Десериализовать JSON в ArrayList <POJO> с помощью Джексона


100

У меня есть класс Java, MyPojoкоторый меня интересует десериализацией из JSON. Я настроил специальный класс MixIn MyPojoDeMixIn, чтобы помочь мне с десериализацией. MyPojoимеет только переменные intи Stringпеременные экземпляра в сочетании с соответствующими методами получения и установки. MyPojoDeMixInвыглядит примерно так:

public abstract class MyPojoDeMixIn {
  MyPojoDeMixIn(
      @JsonProperty("JsonName1") int prop1,
      @JsonProperty("JsonName2") int prop2,
      @JsonProperty("JsonName3") String prop3) {}
}

В моем тестовом клиенте я делаю следующее, но, конечно, это не работает во время компиляции, потому что это JsonMappingExceptionсвязано с несоответствием типов.

ObjectMapper m = new ObjectMapper();
m.getDeserializationConfig().addMixInAnnotations(MyPojo.class,MyPojoDeMixIn.class);
try { ArrayList<MyPojo> arrayOfPojo = m.readValue(response, MyPojo.class); }
catch (Exception e) { System.out.println(e) }

Я знаю, что могу облегчить эту проблему, создав объект «Response», в котором есть только один ArrayList<MyPojo>, но тогда мне придется создавать эти несколько бесполезные объекты для каждого отдельного типа, который я хочу вернуть.

Я также посмотрел онлайн на JacksonInFiveMinutes, но ужасно долго разбирался в Map<A,B>том, что касается моей проблемы и как это связано с ней. Если вы не можете сказать, я совершенно новичок в Java и имею опыт работы с Obj-C. В них конкретно упоминается:

В дополнение к привязке к POJO и «простым» типам существует еще один вариант: привязка к универсальным (типизированным) контейнерам. Этот случай требует особой обработки из-за так называемого стирания типа (используется Java для реализации универсальных шаблонов в некоторой степени обратно совместимым способом), что не позволяет вам использовать что-то вроде Collection.class (которое не компилируется).

Итак, если вы хотите привязать данные к карте, вам нужно будет использовать:

Map<String,User> result = mapper.readValue(src, new TypeReference<Map<String,User>>() { });

Как я могу выполнить десериализацию напрямую в ArrayList?


Ответы:


153

Вы можете десериализовать прямо в список с помощью TypeReferenceоболочки. Пример метода:

public static <T> T fromJSON(final TypeReference<T> type,
      final String jsonPacket) {
   T data = null;

   try {
      data = new ObjectMapper().readValue(jsonPacket, type);
   } catch (Exception e) {
      // Handle the problem
   }
   return data;
}

И используется так:

final String json = "";
Set<POJO> properties = fromJSON(new TypeReference<Set<POJO>>() {}, json);

ТипСсылка Javadoc


Ваш ответ, похоже, связан с их информацией о том, как использовать встроенную поддержку для TypeReference- я просто не понимаю, как это сделать ... Пожалуйста, ознакомьтесь с моим изменением выше, чтобы узнать, как использовать дженерики.
tacos_tacos_tacos

Ну, это связано. Но это отрывок из рабочего кода в продакшене. Забудьте о миксине, просто используйте код, который я показал (но, конечно, замените POJO именем вашего фактического класса bean-компонента).
Perception

Ваш код скомпилирован, но при попытке распечатать файл arrayList.toString()about a NullPointerException. Я предполагаю, что это могло быть потому, что my POJOне соответствует правильным соглашениям об именах для своих свойств, то есть вся проблема в том, что веб-служба возвращается, Prop1Memberа мой объект имеет Prop1. Это единственная реальная причина, по которой я использую миксины для начала, поэтому мне не нужно помещать объявления для @JsonPropertyмоих чистых объектов.
tacos_tacos_tacos

1
Визуально проверьте свой массив, чтобы убедиться, что вы получили хотя бы список. И, если необходимо, снова добавьте миксин, который должен работать вместе с TypeReference, чтобы все было аккуратно десериализовано.
Perception

2
@JsonProperty не такой злой, каким его считают люди. Трудно уйти от аннотаций конкретных производителей, учитывая текущее состояние стандартизации (минимальное) в этой области.
Perception

111

Другой способ - использовать массив как тип, например:

ObjectMapper objectMapper = new ObjectMapper();
MyPojo[] pojos = objectMapper.readValue(json, MyPojo[].class);

Таким образом вы избегаете всех хлопот с объектом Type, и если вам действительно нужен список, вы всегда можете преобразовать массив в список следующим образом:

List<MyPojo> pojoList = Arrays.asList(pojos);

ИМХО это намного читабельнее.

И чтобы сделать его реальным списком (который можно изменить, см. Ограничения Arrays.asList()), просто выполните следующие действия:

List<MyPojo> mcList = new ArrayList<>(Arrays.asList(pojos));

1
Конечно, элегантно, но я не могу создать его из-за класса MyPojo []., Который я не хочу передавать в качестве параметра.
Адриан Реджерс

Я думаю, что использование, TypeFactoryкак описано в следующем ответе: stackoverflow.com/a/42458104/91497 - это способ Джексона указать тип.
Jmini

36

Этот вариант выглядит более простым и элегантным.

CollectionType typeReference =
    TypeFactory.defaultInstance().constructCollectionType(List.class, Dto.class);
List<Dto> resultDto = objectMapper.readValue(content, typeReference);

Спасибо за явную CollectionTypeссылку.
Эрвин Весселс,

3

У меня такая же проблема. У меня есть json, который нужно преобразовать в ArrayList.

Аккаунт выглядит так.

Account{
  Person p ;
  Related r ;

}

Person{
    String Name ;
    Address a ;
}

Все вышеперечисленные классы правильно аннотированы. Я пробовал TypeReference> () {}, но не работает.

Это дает мне Arraylist, но у ArrayList есть connectedHashMap, который содержит еще несколько связанных хэш-карт, содержащих окончательные значения.

Мой код выглядит следующим образом:

public T unmarshal(String responseXML,String c)
{
    ObjectMapper mapper = new ObjectMapper();

    AnnotationIntrospector introspector = new JacksonAnnotationIntrospector();

    mapper.getDeserializationConfig().withAnnotationIntrospector(introspector);

    mapper.getSerializationConfig().withAnnotationIntrospector(introspector);
    try
    {
      this.targetclass = (T) mapper.readValue(responseXML,  new TypeReference<ArrayList<T>>() {});
    }
    catch (JsonParseException e)
    {
      e.printStackTrace();
    }
    catch (JsonMappingException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }

    return this.targetclass;
}

Я наконец решил проблему. Я могу преобразовать список в Json String непосредственно в ArrayList следующим образом:

JsonMarshallerUnmarshaller<T>{

     T targetClass ;

     public ArrayList<T> unmarshal(String jsonString)
     {
        ObjectMapper mapper = new ObjectMapper();

        AnnotationIntrospector introspector = new JacksonAnnotationIntrospector();

        mapper.getDeserializationConfig().withAnnotationIntrospector(introspector);

        mapper.getSerializationConfig().withAnnotationIntrospector(introspector);
        JavaType type = mapper.getTypeFactory().
                    constructCollectionType(ArrayList.class, targetclass.getClass()) ;
        try
        {
        Class c1 = this.targetclass.getClass() ;
        Class c2 = this.targetclass1.getClass() ;
            ArrayList<T> temp = (ArrayList<T>) mapper.readValue(jsonString,  type);
        return temp ;
        }
       catch (JsonParseException e)
       {
        e.printStackTrace();
       }
       catch (JsonMappingException e) {
           e.printStackTrace();
       } catch (IOException e) {
          e.printStackTrace();
       }

     return null ;
    }  

}

2

У меня это работает.

@Test
public void cloneTest() {
    List<Part> parts = new ArrayList<Part>();
    Part part1 = new Part(1);
    parts.add(part1);
    Part part2 = new Part(2);
    parts.add(part2);
    try {
        ObjectMapper objectMapper = new ObjectMapper();
        String jsonStr = objectMapper.writeValueAsString(parts);

        List<Part> cloneParts = objectMapper.readValue(jsonStr, new TypeReference<ArrayList<Part>>() {});
    } catch (Exception e) {
        //fail("failed.");
        e.printStackTrace();
    }

    //TODO: Assert: compare both list values.
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.