Désérialiser JSON en ArrayList <POJO> à l'aide de Jackson

100

J'ai une classe Java MyPojoque je souhaite désérialiser de JSON. J'ai configuré une classe MixIn spéciale MyPojoDeMixIn, pour m'aider dans la désérialisation. MyPojoa seulement intet des Stringvariables d'instance combinées avec des getters et des setters appropriés. MyPojoDeMixInressemble à quelque chose comme ceci:

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

Dans mon client de test, je fais ce qui suit, mais bien sûr cela ne fonctionne pas au moment de la compilation car il y a un JsonMappingExceptionlié à une incompatibilité de type.

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

Je suis conscient que je pourrais atténuer ce problème en créant un objet "Response" qui ne contient qu'un seul objet ArrayList<MyPojo>, mais je devrais alors créer ces objets quelque peu inutiles pour chaque type que je veux retourner.

J'ai également regardé en ligne JacksonInFiveMinutes, mais j'ai eu beaucoup de mal à comprendre le sujet Map<A,B>et son lien avec mon problème. Si vous ne pouvez pas le dire, je suis entièrement nouveau sur Java et je viens d'un arrière-plan Obj-C. Ils mentionnent spécifiquement:

En plus de la liaison aux POJO et aux types "simples", il existe une variante supplémentaire: celle de la liaison à des conteneurs génériques (typés). Ce cas nécessite un traitement spécial en raison de ce qu'on appelle l'effacement de type (utilisé par Java pour implémenter des génériques d'une manière quelque peu rétrocompatible), qui vous empêche d'utiliser quelque chose comme Collection.class (qui ne compile pas).

Donc, si vous souhaitez lier des données dans une carte, vous devrez utiliser:

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

Comment puis-je désérialiser directement vers ArrayList?

tacos_tacos_tacos
la source

Réponses:

151

Vous pouvez désérialiser directement dans une liste à l'aide du TypeReferencewrapper. Un exemple de méthode:

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

Et s'utilise ainsi:

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

TypeRéférence Javadoc

la perception
la source
Votre réponse semble liée à leurs informations sur la façon d'utiliser le support intégré pour TypeReference- je ne comprends tout simplement pas comment le faire ... Veuillez consulter ma modification ci-dessus pour obtenir des instructions sur l'utilisation des génériques.
tacos_tacos_tacos
Eh bien, c'est lié. Mais ceci est un extrait de code de travail en production. Oubliez votre mixin, utilisez simplement le code que j'ai montré (mais remplacez bien sûr POJO par le nom de votre classe de bean).
Perception
Votre code a été compilé, mais j'obtiens une exception d'exécution lorsque j'essaie d'imprimer arrayList.toString()environ un NullPointerException. Je suppose que cela pourrait être dû au fait que mon POJOn'est pas conforme aux bonnes conventions de dénomination pour ses propriétés, c'est-à-dire que tout le problème est que le service Web retourne Prop1Memberet que mon objet l'a Prop1. C'est la seule vraie raison pour laquelle j'utilise des mixins pour commencer, donc je n'ai pas à mettre les déclarations pour @JsonPropertydans mes objets purs.
tacos_tacos_tacos
1
Inspectez visuellement votre tableau pour vous assurer d'avoir au moins une liste. Et si nécessaire, ajoutez le mixin, qui devrait fonctionner avec le TypeReference pour que tout soit parfaitement désérialisé.
Perception
2
@JsonProperty n'est pas aussi mauvais que les gens le prétendent aussi. Il est difficile de s'éloigner des annotations spécifiques aux fournisseurs avec l'état actuel de la normalisation (minime) sur le terrain.
Perception du
108

Une autre façon consiste à utiliser un tableau comme type, par exemple:

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

De cette façon, vous évitez tous les tracas avec l'objet Type, et si vous avez vraiment besoin d'une liste, vous pouvez toujours convertir le tableau en liste en:

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

IMHO c'est beaucoup plus lisible.

Et pour en faire une liste réelle (qui peut être modifiée, voir les limitations de Arrays.asList()), procédez comme suit:

List<MyPojo> mcList = new ArrayList<>(Arrays.asList(pojos));
DevNG
la source
1
Élégant bien sûr, mais je suis incapable de le générer à cause de MyPojo []. Class, que je ne veux pas passer en paramètre.
Adrian Redgers
Je pense TypeFactoryqu'en utilisant comme décrit dans la réponse suivante: stackoverflow.com/a/42458104/91497 est la manière Jackson de spécifier le type.
Jmini
35

Cette variante a l'air plus simple et élégante.

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

J'ai aussi le même problème. J'ai un json qui doit être converti en ArrayList.

Le compte ressemble à ceci.

Account{
  Person p ;
  Related r ;

}

Person{
    String Name ;
    Address a ;
}

Toutes les classes ci-dessus ont été correctement annotées. J'ai essayé TypeReference> () {} mais ne fonctionne pas.

Cela me donne Arraylist mais ArrayList a un linkedHashMap qui contient des hashmaps plus liés contenant des valeurs finales.

Mon code est le suivant:

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

J'ai finalement résolu le problème. Je peux convertir la liste dans Json String directement en ArrayList comme suit:

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
la source
2

Cela fonctionne pour moi.

@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
la source