Десериалізуйте JSON до ArrayList <POJO> за допомогою Джексона


100

У мене є клас Java, MyPojoякий я зацікавлений у десеріалізації з JSON. Я налаштував спеціальний клас MixIn MyPojoDeMixIn, щоб допомогти мені з десериалізацією. MyPojoмає змінні only intта Stringinstance в поєднанні з належними геттерами та сеттерами. 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) }

Я усвідомлюю, що міг би полегшити цю проблему, створивши об’єкт „Відповідь”, який має лише 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?


Відповіді:


151

Ви можете десеріалізувати список безпосередньо за допомогою 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);

TypeReference Javadoc


Ваша відповідь, схоже, пов’язана з їх інформацією про те, як використовувати вбудовану підтримку TypeReference- я просто не розумію, як це зробити ... Будь ласка, перегляньте мою редакцію вище, щоб отримати їхні вказівки щодо використання дженериків.
tacos_tacos_tacos

Ну, це пов’язано. Але це фрагмент робочого коду у виробництві. Забудьте про свій мікс, просто використовуйте код, який я показав (але замініть POJO, звичайно, на ім'я вашого фактичного класу квасолі).
Сприйняття

Ваш код скомпільований, але я отримую виняток часу виконання під час спроби роздрукувати arrayList.toString()приблизно a NullPointerException. Я здогадуюсь, що це могло бути тому, що мій POJOне відповідає правильним правилам іменування для своїх властивостей, тобто вся проблема полягає в тому, що веб-служба повертається Prop1Memberі мій об’єкт має Prop1. Це єдина реальна причина, через яку я використовую mixins для початку, тому мені не потрібно поміщати декларації @JsonPropertyу свої чисті об’єкти.
tacos_tacos_tacos

1
Візуально огляньте свій масив, щоб переконатися, що ви повернули принаймні список. А якщо потрібно, додайте міксин назад, який повинен працювати разом із TypeReference, щоб отримати все акуратно десеріалізовано.
Сприйняття

2
@JsonProperty не настільки злий, як це роблять люди. Важко відійти від конкретних анотацій постачальників, що стосується поточного стану стандартизації (мінімального) у цій галузі.
Сприйняття

108

Інший спосіб - використовувати масив як тип, наприклад:

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

35

Цей варіант виглядає більш простим і елегантним.

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

3

У мене також така сама проблема. У мене є json, який слід перетворити на ArrayList.

Обліковий запис виглядає так.

Account{
  Person p ;
  Related r ;

}

Person{
    String Name ;
    Address a ;
}

Усі вищезазначені класи були анотовані належним чином. Я спробував TypeReference> () {}, але не працює.

Це дає мені Arraylist, але ArrayList має linkedHashMap, який містить ще кілька пов'язаних хеш-карт, що містять кінцеві значення.

Мій код такий:

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.