29 votes

REST avec liaison de données complète Spring et Jackson

Je suis à l'aide de Spring MVC pour gérer JSON requêtes POST. Sous les couvertures, je suis en utilisant le MappingJacksonHttpMessageConverter construit sur l'Jackson JSON processeur et activé lorsque vous utilisez le mvc:annotation-driven.

L'un de mes services reçoit une liste d'actions:

@RequestMapping(value="/executeActions", method=RequestMethod.POST)
    public @ResponseBody String executeActions(@RequestBody List<ActionImpl> actions) {
        logger.info("executeActions");
        return "ACK";
    }

J'ai trouvé que Jackson cartes de la requestBody à une Liste de java.util.LinkedHashMap éléments (simple liaison de données). Au lieu de cela, je voudrais la demande d'être lié à une Liste d'objets de type (dans ce cas "ActionImpl").

Je sais que c'est facile à faire si vous utilisez Jackson ObjectMapper directement:

List<ActionImpl> result = mapper.readValue(src, new TypeReference<List<ActionImpl>>() { }); 

mais je me demandais quelle est la meilleure façon d'atteindre cet objectif lors de l'utilisation de Spring MVC et MappingJacksonHttpMessageConverter. Tous les conseils?

Merci

32voto

Michiel Verkaik Points 681

J'ai constaté que vous pouvez également contourner le problème d'effacement de type en utilisant un tableau comme @RequestBody au lieu d'une collection. Par exemple, ce qui suit fonctionnerait:

 public @ResponseBody String executeActions(@RequestBody ActionImpl[] actions) { //... }
 

23voto

StaxMan Points 34626

Je soupçonne un problème de type de l'effacement, c'est à dire au lieu de passer en paramètre générique type, peut-être seulement des actions.getClass() est passée; et ce serait de donner le type de l'équivalent de List< ?>.

Si cela est vrai, une possibilité serait d'utiliser un intermédiaire sous-classe, comme:

public class ActionImplList extends ArrayList<ActionImpl> { }

car cette volonté de conserver des informations de type, même si seulement la classe est passé. Alors:

public @ResponseBody String executeActions(@RequestBody ActionImplList actions)

ferait l'affaire. Pas optimal, mais il devrait fonctionner.

J'espère que quelqu'un avec plus de Spring MVC connaissances peut jeter de la lumière sur le pourquoi du type de paramètre n'est pas passé (c'est peut-être un bug?), mais au moins, il y a un travail autour de.

7voto

Frédéric Points 243

Pour votre information, la fonctionnalité sera disponible au printemps 3.2 (voir https://jira.springsource.org/browse/SPR-9570 )

Je viens de le tester sur le M2 actuel et cela fonctionne comme un charme prêt à l'emploi (pas besoin de fournir une annotation supplémentaire pour fournir le type paramétré, il sera automatiquement résolu par le nouveau MessageConverter)

2voto

waxwing Points 10190

Cette question est déjà vieux, mais je pense que je peux vous apporter un peu de toute façon.

Comme StaxMan souligné, cela est dû au type d'effacement. Il a certainement devrait être possible, parce que vous pouvez obtenir le générique arguments par réflexion à partir de la définition de la méthode. Cependant, le problème est l'API de la HttpMessageConverter:

T read(Class<? extends T> clazz, HttpInputMessage inputMessage);

Ici, seulement List.class seront passés à la méthode. Donc, comme vous pouvez le voir, il est impossible de mettre en œuvre un HttpMessageConverter qui calcule le type réel en regardant la méthode type de paramètre, ce qui n'est pas disponible.

Néanmoins, il est possible de coder votre propre solution de contournement - vous juste de ne pas être à l'aide de HttpMessageConverter. Spring MVC vous permet d'écrire votre propre WebArgumentResolver que les coups de pied avant de la norme des méthodes de résolution. Vous pouvez par exemple utiliser vos propres annotations (@JsonRequestBody?) qui utilise directement un ObjectMapper pour analyser votre valeur. Vous serez en mesure de fournir le type de paramètre de la méthode:

final Type parameterType= method.getParameterTypes()[index];
List<ActionImpl> result = mapper.readValue(src, new TypeReference<Object>>() {
    @Override
    public Type getType() {
        return parameterType;
    }
});

Pas vraiment la façon dont TypeReference était destiné à être utilisé je présume, mais ObjectMapper ne fournit pas une méthode plus appropriée.

0voto

skaffman Points 197885

Avez-vous essayé de déclarer la méthode comme:

 executeActions(@RequestBody TypeReference<List<ActionImpl>> actions)
 

Je ne l' ai pas essayé, mais en fonction de votre question , il est la première chose que je voudrais essayer.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X