Deserialisasi JSON ke ArrayList <POJO> menggunakan Jackson

100

Saya memiliki kelas Java MyPojoyang ingin saya deserialisasi dari JSON. Saya telah mengonfigurasi kelas MixIn khusus,, MyPojoDeMixInuntuk membantu saya dengan deserialisasi. MyPojohanya memiliki intdan Stringvariabel instan digabungkan dengan pengambil dan penyetel yang tepat. MyPojoDeMixInterlihat seperti ini:

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

Di klien uji saya, saya melakukan hal berikut, tetapi tentu saja itu tidak berfungsi pada waktu kompilasi karena ada yang JsonMappingExceptionterkait dengan ketidakcocokan tipe.

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) }

Saya sadar bahwa saya dapat mengatasi masalah ini dengan membuat objek "Respons" yang hanya memiliki satu ArrayList<MyPojo>di dalamnya, tetapi kemudian saya harus membuat objek yang agak tidak berguna ini untuk setiap jenis yang ingin saya kembalikan.

Saya juga melihat JacksonInFiveMinutes secara online tetapi kesulitan memahami hal-hal tentang Map<A,B>dan bagaimana kaitannya dengan masalah saya. Jika Anda tidak tahu, saya sama sekali baru mengenal Java dan berasal dari latar belakang Obj-C. Mereka secara khusus menyebutkan:

Selain mengikat ke POJO dan tipe "sederhana", ada satu varian tambahan: yang mengikat ke kontainer generik (diketik). Kasus ini memerlukan penanganan khusus karena apa yang disebut Type Erasure (digunakan oleh Java untuk mengimplementasikan generik dengan cara yang agak kompatibel ke belakang), yang mencegah Anda menggunakan sesuatu seperti Collection.class (yang tidak dapat dikompilasi).

Jadi, jika Anda ingin mengikat data ke dalam Peta, Anda perlu menggunakan:

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

Bagaimana saya bisa melakukan deserialisasi langsung ke ArrayList?

tacos_tacos_tacos
sumber

Jawaban:

151

Anda dapat melakukan deserialisasi langsung ke daftar dengan menggunakan TypeReferencepembungkus. Metode contoh:

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;
}

Dan digunakan sebagai berikut:

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

TypeReference Javadoc

Persepsi
sumber
Jawaban Anda tampaknya terkait dengan info mereka tentang cara menggunakan dukungan bawaan untuk TypeReference- Saya hanya tidak mengerti cara melakukannya ... Silakan lihat hasil edit saya di atas untuk instruksi mereka tentang cara menggunakan obat generik.
tacos_tacos_tacos
Nah, itu terkait. Tetapi ini adalah cuplikan dari kode kerja dalam produksi. Lupakan tentang mixin Anda, cukup gunakan kode yang telah saya tunjukkan (tetapi tentu saja ganti POJO dengan nama kelas kacang Anda yang sebenarnya).
Persepsi
Kode Anda telah dikompilasi, tetapi saya mendapatkan pengecualian waktu proses saat mencoba mencetak arrayList.toString()tentang a NullPointerException. Saya menduga bahwa ini bisa jadi karena my POJOtidak sesuai dengan konvensi penamaan yang tepat untuk propertinya, yaitu, seluruh masalah adalah bahwa layanan web kembali Prop1Memberdan objek saya memilikinya Prop1. Ini adalah satu-satunya alasan sebenarnya saya menggunakan mixin untuk memulai, jadi saya tidak harus meletakkan deklarasi untuk @JsonPropertyobjek murni saya.
tacos_tacos_tacos
1
Periksa secara visual array Anda untuk memastikan Anda mendapatkan kembali daftar setidaknya. Dan jika perlu, tambahkan kembali mixin, yang akan bekerja bersama TypeReference untuk mendapatkan deserialisasi yang rapi.
Persepsi
2
@JsonProty tidak seburuk yang dibayangkan orang. Sulit untuk melepaskan diri dari anotasi khusus vendor apa dengan keadaan standarisasi saat ini (minimal) di lapangan.
Persepsi
108

Cara lain adalah dengan menggunakan array sebagai tipe, misalnya:

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

Dengan cara ini Anda menghindari semua kerumitan dengan objek Type, dan jika Anda benar-benar membutuhkan daftar, Anda selalu dapat mengonversi larik menjadi daftar dengan:

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

IMHO ini jauh lebih mudah dibaca.

Dan untuk membuatnya menjadi daftar yang sebenarnya (yang dapat dimodifikasi, lihat batasannya Arrays.asList()) maka cukup lakukan hal berikut:

List<MyPojo> mcList = new ArrayList<>(Arrays.asList(pojos));
DevNG
sumber
1
Elegan pasti, tetapi saya tidak dapat membuatnya karena kelas MyPojo []., Yang tidak ingin saya berikan sebagai parameter.
Adrian Redgers
Saya pikir menggunakan TypeFactoryseperti yang dijelaskan dalam jawaban berikutnya: stackoverflow.com/a/42458104/91497 adalah cara Jackson untuk menentukan jenisnya.
Jmini
36

Varian ini terlihat lebih simpel dan elegan.

CollectionType typeReference =
    TypeFactory.defaultInstance().constructCollectionType(List.class, Dto.class);
List<Dto> resultDto = objectMapper.readValue(content, typeReference);
Дмитрий Кулешов
sumber
Terima kasih atas CollectionTypereferensi eksplisitnya .
Erwin Wessels
3

Saya juga mengalami masalah yang sama. Saya memiliki json yang akan diubah ke ArrayList.

Akun terlihat seperti ini.

Account{
  Person p ;
  Related r ;

}

Person{
    String Name ;
    Address a ;
}

Semua kelas di atas telah dianotasi dengan benar. Saya telah mencoba TypeReference> () {} tetapi tidak berhasil.

Ini memberi saya Arraylist tetapi ArrayList memiliki linkedHashMap yang berisi beberapa hashmaps tertaut yang berisi nilai akhir.

Kode saya adalah sebagai berikut:

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;
}

Saya akhirnya memecahkan masalah. Saya dapat mengonversi Daftar di Json String langsung ke ArrayList sebagai berikut:

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 ;
    }  

}
rushidesai1
sumber
2

Ini berhasil untuk saya.

@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.
}
Jugal Panchal
sumber