Deserialize JSON เป็น ArrayList <POJO> โดยใช้ Jackson


100

ฉันมีคลาส Java MyPojoที่ฉันสนใจที่จะ deserializing จาก JSON ฉันได้กำหนดค่าคลาส MixIn พิเศษMyPojoDeMixInเพื่อช่วยฉันในการ deserialization MyPojoมีเฉพาะintและStringตัวแปรอินสแตนซ์รวมกับ getters และ setters ที่เหมาะสม 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 และประเภท "แบบง่าย" แล้วยังมีตัวแปรเพิ่มเติมอีกหนึ่งตัวแปรนั่นคือการผูกกับคอนเทนเนอร์ทั่วไป (พิมพ์) กรณีนี้ต้องการการจัดการพิเศษเนื่องจากสิ่งที่เรียกว่า Type Erasure (Java ใช้เพื่อใช้ generics ในลักษณะที่เข้ากันได้ค่อนข้างย้อนหลัง) ซึ่งป้องกันไม่ให้คุณใช้บางอย่างเช่น Collection.class (ซึ่งไม่รวบรวม)

ดังนั้นหากคุณต้องการผูกข้อมูลลงในแผนที่คุณจะต้องใช้:

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

ฉันจะ deserialize โดยตรงได้ArrayListอย่างไร?


คำตอบ:


154

คุณสามารถ deserialize โดยตรงไปยังรายการโดยใช้TypeReferenceWrapper วิธีการตัวอย่าง:

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

มันเกี่ยวข้องกัน แต่นี่เป็นข้อมูลโค้ดจากรหัสการทำงานในการผลิต ลืมเกี่ยวกับ mixin ของคุณเพียงใช้รหัสที่ฉันแสดง (แต่แทนที่ POJO ด้วยชื่อคลาส bean จริงของคุณ)
Perception

คอมไพล์โค้ดของคุณแล้ว แต่ฉันได้รับข้อยกเว้นรันไทม์เมื่อพยายามพิมพ์arrayList.toString()เกี่ยวกับไฟล์NullPointerException. ฉันคาดเดาว่าเรื่องนี้อาจจะเป็นเพราะฉันPOJOไม่สอดคล้องกับการตั้งชื่อที่เหมาะสมสำหรับคุณสมบัติของมันที่เป็นปัญหาทั้งหมดคือผลตอบแทนที่บริการเว็บและวัตถุของฉันมีProp1Member Prop1นี่เป็นเหตุผลเดียวที่แท้จริงที่ฉันใช้มิกซ์อินเริ่มต้นดังนั้นฉันจึงไม่ต้องใส่คำประกาศ@JsonPropertyในวัตถุบริสุทธิ์ของฉัน
tacos_tacos_tacos

1
ตรวจสอบอาร์เรย์ของคุณด้วยสายตาเพื่อให้แน่ใจว่าคุณได้รับรายการกลับมาอย่างน้อย และหากจำเป็นต้องเพิ่ม mixin กลับซึ่งควรทำงานร่วมกับ TypeReference เพื่อให้ทุกอย่างถูก deserialized
การรับรู้

2
@JsonProperty ไม่ได้ชั่วร้ายอย่างที่ผู้คนทำออกมาเช่นกัน เป็นการยากที่จะหลีกเลี่ยงคำอธิบายประกอบเฉพาะของผู้ขายซึ่งมีสถานะปัจจุบันของการกำหนดมาตรฐาน (ขั้นต่ำ) ในฟิลด์
การรับรู้

111

อีกวิธีหนึ่งคือการใช้อาร์เรย์เป็นประเภทเช่น:

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

ด้วยวิธีนี้คุณจะหลีกเลี่ยงความยุ่งยากทั้งหมดกับวัตถุ Type และหากคุณต้องการรายการจริงๆคุณสามารถแปลงอาร์เรย์เป็นรายการโดย:

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

IMHO นี่อ่านง่ายกว่ามาก

และเพื่อให้เป็นรายการจริง (ที่สามารถแก้ไขได้ดูข้อ จำกัด ของArrays.asList()) จากนั้นทำสิ่งต่อไปนี้:

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

1
สวยแน่นอน แต่ฉันไม่สามารถระบุได้เนื่องจากคลาส MyPojo [] ซึ่งฉันไม่ต้องการส่งผ่านเป็นพารามิเตอร์
Adrian Redgers

ฉันคิดว่าการใช้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ข้อมูลอ้างอิงที่ชัดเจน
Erwin Wessels

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

ในที่สุดฉันก็แก้ปัญหาได้ ฉันสามารถแปลง List ใน 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.
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.