2
votes

Le cas du serpent de désérialisation de Spring Boot en cas de chameau échoue. Impossible de désérialiser "some_value" en "someValue"

J'ai donc cette application Spring Boot qui doit accepter une requête GET avec les paramètres "some_value = 1500 & some_other_value = 50000" à un objet avec les attributs someValue et someOtherValue.

J'ai essayé @JsonProperty ("some_value") et cela n'a pas fonctionné. J'ai ajouté "spring.jackson.property-naming-strategy = SNAKE_CASE" à mon fichier application.properties et cela ne fonctionne toujours pas.

Détail important: lorsque j'essaie de sérialiser un objet, il devient someValue => some_value et someOtherValue => some_other_value. Donc je sais que la config est "bien" mais elle refuse de mapper le paramètre de demande dans le cas du serpent au cas du chameau dont j'ai besoin. (Et non ... je n'ai aucun contrôle sur le format de la requête. J'obtiens les paramètres dans le cas du serpent et je dois les mapper sur le cas du chameau)

Veuillez aider. Merci!


4 commentaires

Je ne pense pas que la désérialisation fonctionnera pour les requêtes GET . Il correspond simplement aux types de paramètres de requête respectifs ou à une simple carte des paramètres de requête. Partagez votre code de mappage de demande.


Je n'en ai pas ... Je suis tellement frustré que je l'ai nettoyé à nu juste pour le tester. J'obtiens localhost: 8080 / myApp / users? User = pepe & some_value = 1500 & some_ot‌ her_value = 50000 mon code est @GetMapping ("/ users") public User users (User u) {return u ; } et qui renvoie {"user": "pepe", "some_value": null, "some_other_value": null}


Comme je l'ai dit, cela ne fonctionnera pas pour la demande GET. changez-le pour publier et envoyez l'utilisateur json dans le corps de la requête. @PostMapping ("/ users") public User users (@RequestBody User u) {return u; } .


FFS c'était ça ... Honnêtement, je ne vois pas pourquoi cela devrait fonctionner uniquement sur la publication mais pas sur obtenir ... cela semble contre-intuitif. Mais c'était ça. Merci un million, honnêtement, je ne trouve aucune information indiquant "ceci est uniquement pour le corps du message et non pour obtenir les paramètres" Merci beaucoup.


4 Réponses :


0
votes

Donc @Barath avait raison, vous n'obtenez pas des objets désérialisés propres à partir des paramètres GET. Mais il y a une solution de contournement. Je ne sais pas dans quelle mesure légal c'est ou non ... mais voilà:

@Autowired private ObjectMapper objectMapper;

@GetMapping("/users")
    public User users(@RequestParam Map<String,String> params){
        User u = objectMapper.convertValue(params,User.class);
        return u;
    }

De cette façon, vous avez toujours votre méthode GET et vous obtenez tous les paramètres désérialisés dans un joli petit objet pour que vous fassiez n'importe quoi. J'espère que cela aide n'importe qui. Et j'espère que les gens de Spring / Jackson activeront la désérialisation automatique des paramètres GET de la même manière que cela fonctionne avec les méthodes POST.


0 commentaires

0
votes

Premièrement, vos paramètres de requête ( some_value = 1500 & some_other_value = 50000 ) sont mappés sur RequestParam, pas RequestBody. Deuxièmement, la bibliothèque jackson désérialise RequestBody, pas RequestParam. Troisièmement, publier ou mettre le paramètre de mappage de méthode http dans le corps.

Donc, vous demandez la méthode GET et les paramètres de requête et votre configuration jackson ne fonctionne pas avec vos paramètres de requête.

Le paramètre de cas Camel, ou @RequestMapping (name = "some_param") String someParam sera mappé à votre paramètre de requête.


1 commentaires

J'apprécie la réponse mais ce n'était pas mon problème. Je sais déjà que ce sont des @RequestParams ... et non un corps ... et que si je reçois une demande GET, il ne devrait pas avoir de corps ... mais je ne suis pas sûr de ce que vous essayez même de dire . Si je passe tous les paramètres conformes à un POJO, j'obtiens bien l'instance POJO ... quelque chose (sinon jackson) fait le mappage. Et en ignorant ma configuration de propertynamestrategies. c'était mon problème. Je l'ai déjà fait fonctionner en utilisant une solution de contournement dans ma réponse. Je ne suis pas sûr de comprendre votre argument.



2
votes

Une autre façon est de créer un HandlerMethodArgumentResolver . C'est verbeux, mais vous pouvez gérer la chaîne de requête, injecter l'ObjectMapper (en gardant la même configuration) et faire vous-même la conversion.

J'ai créé une annotation pour filtrer le type que je veux gérer:

@Configuration
public class ArgumentResolverConfig implements WebMvcConfigurer {
    @Autowired
    private QueryStringArgumentResolver argumentResolver;

    @Override
    public void addArgumentResolvers(
            final List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(argumentResolver);
    }
}

Puis créé le résolveur:

@ResponseStatus(HttpStatus.OK)
    @GetMapping("/some-url")
    public SomeResponse doSomething(@QueryStringArgResolver final SomeQueryStringToBind request) {
        ...
    }

Utilisation:

@Component
public class QueryStringArgumentResolver implements HandlerMethodArgumentResolver {

...

    @Autowired
    private ObjectMapper mapper;

    @Override
    public boolean supportsParameter(final MethodParameter methodParameter) {
        return methodParameter.getParameterAnnotation(QueryStringArgResolver.class) != null;
    }

    @Override
    public Object resolveArgument(final MethodParameter methodParameter,
                                  final ModelAndViewContainer modelAndViewContainer,
                                  final NativeWebRequest nativeWebRequest,
                                  final WebDataBinderFactory webDataBinderFactory) throws Exception {

        final HttpServletRequest request = (HttpServletRequest) nativeWebRequest.getNativeRequest();
        final String json = qs2json(request.getQueryString());
        final Object a = mapper.readValue(json, methodParameter.getParameterType());

        return a;
    }

...
}

BTW, ne oubliez d'enregistrer votre résolveur de cette manière pour pouvoir injecter des beans:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface QueryStringArgResolver {
}


0 commentaires

2
votes

Je pense que vous avez une erreur de configuration, dans mon cas, la cause est l'annotation @EnableWebMvc, si vous utilisez cette annotation, l'autoconfig par défaut de spring boot ne fonctionnera pas.

afin de convertir l'attribut de camelCase en snake_case , il vous suffit de remplacer le bean jackson2ObjectMapperBuilder , utilisez ce bean pour changer le paramètre snake_case pour jackson, essayez ces codes ci-dessous, ajoutez-les à votre fichier @Configuration

@Bean
    @Primary
    public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder() {
        Jackson2ObjectMapperBuilder jsonBuilderConfig = new Jackson2ObjectMapperBuilder();
        jsonBuilderConfig.propertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
        return jsonBuilderConfig;
    }


1 commentaires

J'ai passé environ 3 heures à tester toutes les façons possibles de changer la stratégie de dénomination en cas Snake. Aucun d'entre eux n'a fonctionné, et ... j'ai trouvé votre réponse sur l'annotation @EnableWebMvc. Je ne savais même pas que cela aurait de tels effets secondaires. Merci mec! Maintenant, tout fonctionne comme prévu.