Deserializza JSON in ArrayList <POJO> utilizzando Jackson


97

Ho una classe Java MyPojoche mi interessa deserializzare da JSON. Ho configurato una classe MixIn speciale MyPojoDeMixIn, per aiutarmi con la deserializzazione. MyPojoha solo variabili di istanza inte Stringcombinate con getter e setter appropriati. MyPojoDeMixInassomiglia a questo:

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

Nel mio client di prova faccio quanto segue, ma ovviamente non funziona in fase di compilazione perché c'è un problema JsonMappingExceptionrelativo a una mancata corrispondenza di tipo.

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

Sono consapevole che potrei alleviare questo problema creando un oggetto "Risposta" che contiene solo un ArrayList<MyPojo>, ma poi dovrei creare questi oggetti un po 'inutili per ogni singolo tipo che voglio restituire.

Ho anche guardato online JacksonInFiveMinutes, ma ho passato dei momenti terribili a capire le cose Map<A,B>e come si relazionano al mio problema. Se non puoi dirlo, sono completamente nuovo in Java e provengo da un background Obj-C. Menzionano specificamente:

Oltre all'associazione a POJO e tipi "semplici", esiste un'ulteriore variante: quella di associazione a contenitori generici (tipizzati). Questo caso richiede una gestione speciale a causa del cosiddetto Type Erasure (utilizzato da Java per implementare i generici in un modo piuttosto compatibile con le versioni precedenti), che impedisce di utilizzare qualcosa come Collection.class (che non si compila).

Quindi, se vuoi legare i dati in una mappa, dovrai usare:

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

Come posso deserializzare direttamente in ArrayList?


Risposte:


149

È possibile deserializzare direttamente in un elenco utilizzando il TypeReferencewrapper. Un metodo di esempio:

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

Ed è usato così:

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

TypeReference Javadoc


La tua risposta sembra correlata alle loro informazioni su come utilizzare il supporto integrato per TypeReference- semplicemente non capisco come farlo ... Si prega di vedere la mia modifica sopra per le loro istruzioni su come utilizzare i generici.
tacos_tacos_tacos

Bene, è correlato. Ma questo è uno snippet del codice funzionante in produzione. Dimentica il tuo mixin, usa semplicemente il codice che ho mostrato (ma sostituisci POJO ovviamente con il nome della tua attuale classe di bean).
Perception

Il tuo codice è stato compilato, ma ottengo un'eccezione in fase di esecuzione quando tento di stampare il file arrayList.toString()about a NullPointerException. Immagino che ciò possa essere dovuto al fatto che my POJOnon è conforme alle giuste convenzioni di denominazione per le sue proprietà, ovvero, l'intero problema è che il servizio Web ritorna Prop1Membere il mio oggetto ha Prop1. Questa è l'unica vera ragione per cui sto usando i mixin per cominciare, quindi non devo inserire le dichiarazioni per @JsonPropertynei miei oggetti puri.
tacos_tacos_tacos

1
Ispeziona visivamente il tuo array per assicurarti di aver recuperato almeno un elenco. E se necessario, aggiungi nuovamente il mixin, che dovrebbe funzionare insieme a TypeReference per deserializzare tutto in modo ordinato.
Perception

2
@JsonProperty non è così malvagia come la gente pensa che sia. È difficile allontanarsi dalle annotazioni specifiche del fornitore con l'attuale stato di standardizzazione (minimo) nel campo.
Perception

104

Un altro modo è usare un array come tipo, ad esempio:

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

In questo modo eviti tutte le seccature con l'oggetto Type e se hai davvero bisogno di un elenco puoi sempre convertire l'array in un elenco:

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

IMHO questo è molto più leggibile.

E per renderlo un elenco effettivo (che può essere modificato, vedere i limiti di Arrays.asList()), fai semplicemente quanto segue:

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

1
Elegante certo, ma non riesco a generarlo a causa della classe MyPojo []., Che non voglio passare come parametro.
Adrian Redgers

Penso che usare TypeFactorycome descritto nella risposta successiva: stackoverflow.com/a/42458104/91497 sia il modo Jackson per specificare il tipo.
Jmini

32

Questa variante sembra più semplice ed elegante.

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

3

Anch'io sto avendo lo stesso problema. Ho un json che deve essere convertito in ArrayList.

L'account ha questo aspetto.

Account{
  Person p ;
  Related r ;

}

Person{
    String Name ;
    Address a ;
}

Tutte le classi precedenti sono state annotate correttamente. Ho provato TypeReference> () {} ma non funziona.

Mi dà Arraylist ma ArrayList ha una linkedHashMap che contiene altre hashmap collegate contenenti i valori finali.

Il mio codice è come segue:

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

Finalmente ho risolto il problema. Sono in grado di convertire l'elenco in Json String direttamente in ArrayList come segue:

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

Questo funziona per me.

@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.
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.