web-dev-qa-db-fra.com

Spring / json: convertir une collection typée comme List <MyPojo>

J'essaie de rassembler une liste: List<Pojo> Objets via le modèle Spring Rest.

Je peux transmettre des objets Pojo simples, mais je ne trouve aucune documentation décrivant comment envoyer un objet List<Pojo>.

Spring utilise Jackson JSON pour implémenter le HttpMessageConverter. La documentation jackson couvre ceci:

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

Donc, si vous souhaitez lier des données dans un Map<String,User>, Vous devrez utiliser:

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

TypeReference n'est nécessaire que pour passer la définition de type générique (via une classe interne quelconque dans ce cas): la partie importante est <Map<String,User>> qui définit le type auquel se lier.

Cela peut-il être accompli dans le modèle Spring? J'ai jeté un coup d'œil au code et ça ne me fait rien, mais peut-être que je ne connais pas de truc.


Solution

La solution ultime, grâce aux réponses utiles ci-dessous, était de ne pas envoyer une liste, mais plutôt d'envoyer un seul objet qui étend simplement une liste, comme: class PojoList extends ArrayList<Pojo>. Spring peut réussir à rassembler cet objet, et il accomplit la même chose que l'envoi d'un List<Pojo>, Bien qu'il soit un peu moins propre d'une solution. J'ai également publié un JIRA au printemps pour qu'ils corrigent cette lacune dans leur interface HttpMessageConverter.

33
David Parks

Si je lis la documentation pour MappingJacksonHttpMessageConverter à droite, vous devrez créer et enregistrer une sous-classe de MappingJacksonHttpMessageConverter et remplacer la getJavaType(Class<?>) méthode:

Renvoie le Jackson JavaType pour la classe spécifique. L'implémentation par défaut renvoie TypeFactory.type (Java.lang.reflect.Type), mais cela peut être remplacé dans les sous-classes, pour permettre une gestion de collection générique personnalisée. Par exemple:

protected JavaType getJavaType(Class<?> clazz) {
   if (List.class.isAssignableFrom(clazz)) {
     return TypeFactory.collectionType(ArrayList.class, MyBean.class);
   } else {
     return super.getJavaType(clazz);
   }
}
10

Dans Spring 3.2 il y a maintenant un support pour les types génériques utilisant les nouvelles méthodes exchange()- sur les RestTemplate:

 ParameterizedTypeReference<List<MyBean>> typeRef = new ParameterizedTypeReference<List<MyBean>>() {};
 ResponseEntity<List<MyBean>> response = template.exchange("http://example.com", HttpMethod.GET, null, typeRef);

Fonctionne comme un charme!

40
oehmiche

Une façon de s'assurer que les paramètres de type générique sont inclus est de sous-classer réellement le type List ou Map, de sorte que vous ayez quelque chose comme:

static class MyStringList extends ArrayList<String> { }

et retourner l'instance de cette liste.

Alors pourquoi cela fait-il une différence? Parce que les informations de type générique sont conservées à quelques endroits: déclarations de méthode et de champ et déclarations de super type. Ainsi, alors que la liste "brute" n'inclut aucune information de type d'exécution, la définition de classe de "MyStringList" le fait, via ses déclarations de supertype. Notez que les affectations à des variables apparemment typées n'aident pas: cela crée simplement plus de sucre syntaxique au moment de la compilation: les informations de type réel ne sont transmises qu'avec les instances de classe (ou leurs extensions fournies par lib, comme JavaType et TypeReference dans le cas de Jackson).

En dehors de cela, vous devrez trouver comment passer Jackson JavaType ou TypeReference pour accompagner la valeur.

23
StaxMan