Deserialisieren Sie JSON mit Jackson in ArrayList <POJO>

97

Ich habe eine Java-Klasse MyPojo, die ich von JSON deserialisieren möchte. Ich habe eine spezielle MixIn-Klasse konfiguriert MyPojoDeMixIn, um mich bei der Deserialisierung zu unterstützen. MyPojohat nur intund StringInstanzvariablen mit richtigen Gettern und Setzern kombiniert. MyPojoDeMixInsieht ungefähr so ​​aus:

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

In meinem Testclient mache ich Folgendes, aber es funktioniert natürlich nicht zur Kompilierungszeit, da es einen JsonMappingExceptionZusammenhang mit einer Typinkongruenz gibt.

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

Ich bin mir bewusst, dass ich dieses Problem beheben könnte, indem ich ein "Antwort" -Objekt erstelle, das nur ein Objekt enthält. ArrayList<MyPojo>Dann müsste ich diese etwas nutzlosen Objekte für jeden einzelnen Typ erstellen, den ich zurückgeben möchte.

Ich habe mir auch JacksonInFiveMinutes online angesehen , hatte aber eine schreckliche Zeit damit, die Dinge zu verstehen Map<A,B>und zu verstehen, wie sie mit meinem Problem zusammenhängen. Wenn Sie nicht sagen können, bin ich völlig neu in Java und komme aus einem Obj-C-Hintergrund. Sie erwähnen ausdrücklich:

Neben der Bindung an POJOs und "einfache" Typen gibt es eine weitere Variante: die Bindung an generische (typisierte) Container. Dieser Fall erfordert eine spezielle Behandlung aufgrund der sogenannten Typlöschung (die von Java verwendet wird, um Generika auf etwas abwärtskompatible Weise zu implementieren), wodurch verhindert wird, dass Sie etwas wie Collection.class verwenden (das nicht kompiliert wird).

Wenn Sie also Daten in eine Karte binden möchten, müssen Sie Folgendes verwenden:

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

Wie kann ich direkt deserialisieren ArrayList?

tacos_tacos_tacos
quelle

Antworten:

149

Sie können mithilfe des TypeReferenceWrappers direkt in eine Liste deserialisieren . Eine Beispielmethode:

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

Und wird so verwendet:

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

TypeReference Javadoc

Wahrnehmung
quelle
Ihre Antwort scheint mit den Informationen zur Verwendung der integrierten Unterstützung für zu zusammenhängen. TypeReferenceIch verstehe nur nicht, wie das geht. Anweisungen zur Verwendung von Generika finden Sie oben in meiner Bearbeitung.
Tacos_tacos_tacos
Nun, es ist verwandt. Dies ist jedoch ein Ausschnitt aus dem Arbeitscode in der Produktion. Vergessen Sie Ihr Mixin, verwenden Sie einfach den Code, den ich gezeigt habe (aber ersetzen Sie POJO natürlich durch den Namen Ihrer tatsächlichen Bohnenklasse).
Wahrnehmung
Ihr Code wurde kompiliert, aber ich erhalte eine Laufzeitausnahme, wenn ich versuche, das arrayList.toString()ungefähr a auszudrucken NullPointerException. Ich vermute, dass dies daran liegen könnte, dass my POJOnicht den richtigen Namenskonventionen für seine Eigenschaften entspricht, das heißt, das ganze Problem ist, dass der Webdienst zurückkehrt Prop1Memberund mein Objekt hat Prop1. Dies ist der einzige wirkliche Grund, warum ich zunächst Mixins verwende, damit ich die Deklarationen nicht @JsonPropertyin meine reinen Objekte einfügen muss.
Tacos_tacos_tacos
1
Überprüfen Sie Ihr Array visuell, um sicherzustellen, dass Sie mindestens eine Liste erhalten haben. Und wenn Notwendigkeit , den mixin wieder hinzufügen, die sollte mit dem TypeReference arbeiten zusammen , um alles ordentlich deserialisiert.
Wahrnehmung
2
@JsonProperty ist nicht so böse, wie die Leute es auch ausmachen. Es ist schwierig, sich von herstellerspezifischen Anmerkungen zu lösen, was mit dem aktuellen Stand der Standardisierung (minimal) vor Ort zu tun hat.
Wahrnehmung
104

Eine andere Möglichkeit besteht darin, ein Array als Typ zu verwenden, z.

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

Auf diese Weise vermeiden Sie den Ärger mit dem Type-Objekt. Wenn Sie wirklich eine Liste benötigen, können Sie das Array jederzeit in eine Liste konvertieren, indem Sie:

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

IMHO ist dies viel besser lesbar.

Und um es zu einer tatsächlichen Liste zu machen (die geändert werden kann, siehe Einschränkungen von Arrays.asList()), gehen Sie einfach wie folgt vor:

List<MyPojo> mcList = new ArrayList<>(Arrays.asList(pojos));
DevNG
quelle
1
Elegant sicher, aber ich kann es aufgrund der MyPojo []. Klasse nicht generieren, die ich nicht als Parameter übergeben möchte.
Adrian Redgers
Ich denke, TypeFactorywie in der nächsten Antwort beschrieben zu verwenden: stackoverflow.com/a/42458104/91497 ist die Jackson-Methode, um den Typ anzugeben.
Jmini
31

Diese Variante sieht einfacher und eleganter aus.

CollectionType typeReference =
    TypeFactory.defaultInstance().constructCollectionType(List.class, Dto.class);
List<Dto> resultDto = objectMapper.readValue(content, typeReference);
Дмитрий Кулешов
quelle
3

Ich habe auch das gleiche Problem. Ich habe einen JSON, der in ArrayList konvertiert werden soll.

Konto sieht so aus.

Account{
  Person p ;
  Related r ;

}

Person{
    String Name ;
    Address a ;
}

Alle oben genannten Klassen wurden ordnungsgemäß mit Anmerkungen versehen. Ich habe TypeReference> () {} ausprobiert, funktioniert aber nicht.

Es gibt mir Arraylist, aber ArrayList hat eine linkedHashMap, die einige weitere verknüpfte Hashmaps enthält, die Endwerte enthalten.

Mein Code lautet wie folgt:

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

Ich habe das Problem endlich gelöst. Ich kann die Liste in Json String wie folgt direkt in ArrayList konvertieren:

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
quelle
2

Das funktioniert bei mir.

@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
quelle