【问题标题】:Spring Boot deserialization snake case to camel case fails. Can't deserialize "some_value" to "someValue"Spring Boot 将蛇案例反序列化为骆驼案例失败。无法将“some_value”反序列化为“someValue”
【发布时间】:2019-08-07 18:44:16
【问题描述】:

所以我有这个 Spring Boot 应用程序,它必须将带有参数“some_value=1500&some_other_value=50000”的 GET 请求发送到具有 someValue 和 someOtherValue 属性的对象。

我已经尝试过@JsonProperty("some_value"),但没有成功。我已将“spring.jackson.property-naming-strategy=SNAKE_CASE”添加到我的 application.properties 文件中,但它仍然无法正常工作。

重要细节:当我尝试序列化一个对象时,它确实会变成 someValue => some_value 和 someOtherValue => some_other_value。所以我知道配置“很好”,但它拒绝将蛇案例中的请求参数映射到我需要的骆驼案例。 (不......我无法控制请求格式。我在蛇案例中获取参数,我必须将它们映射到骆驼案例)

请帮忙。谢谢!

【问题讨论】:

  • 我认为反序列化不适用于GET 请求。它只是映射到相应的请求参数类型或简单的请求参数映射。分享您的请求映射代码。
  • 我没有...我很沮丧,以至于我将它清理为准系统只是为了测试。我得到localhost:8080/myApp/users?user=pepe&some_value=1500&some_other_value=50000 我的代码是@GetMapping("/users") public User users(User u){ return u; } 并返回{"user":"pepe","some_value":null,"some_other_value":null}
  • 正如我所说,它不适用于 GET 请求。将其更改为在请求正文中发布和发送用户 json。 @PostMapping("/users") public User users(@RequestBody User u){ return u; }.
  • FFS 就是这样......老实说,我不明白为什么它只适用于 post 而不是 get......这感觉违反直觉。但就是这样。谢谢一百万,老实说,我找不到任何指定“这仅用于帖子正文而不用于获取参数”的信息,非常感谢。

标签: java spring spring-boot jackson


【解决方案1】:

我一直在寻找类似问题的解决方案,但由于我没有找到任何简单、令人满意的答案(其中大多数有很多代码,或者需要我更改 HTTP 方法),我找到了一种方法让我满意。

如果你有这样的控制器方法:

@GetMapping
public ResponseEntity<Connection> handle(SampleDTO sample)

那么你可以在 SampleDTO 中使用@ConstructorProperties over constructor 来解决这个问题:

public class SampleDTO {
    String testVarOne;
    String testVarTwo;

    @ConstructorProperties({ "test_var_one", "test_var_two" })
    public SampleDTO(String testVarOne, String testVarTwo) {
        this.testVarOne = testVarOne;
        this.testVarTwo = testVarTwo;
}

当然,这只有在您知道请求参数名称的情况下才有效。适用于 POST 和 GET 方法。

【讨论】:

    【解决方案2】:

    我认为你在配置中有一些错误,在我的情况下,原因是@EnableWebMvc 注解,如果你使用这个注解,spring boot 的默认自动配置将不起作用。

    为了将属性从camelCase转换为snake_case,你只需要覆盖beanjackson2ObjectMapperBuilder,spring使用这个bean来改变jackson的snake_case设置,试试下面的这些代码将它们添加到你的@Configuration文件中

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

    如果你读过AbstractJackson2HttpMessageConverter类下的readJavaType()方法,你可以看到jackson是如何工作的,有一个objectMapper用来转换json.track这个objectMapper,如果PropertyNamingStrategy没有snake_case设置转换无法工作

    【讨论】:

    • 我花了大约 3 个小时来测试将命名策略更改为 Snake 大小写的所有可能方法。他们都没有工作,然后......然后我找到了你关于 @EnableWebMvc 注释的答案。我什至不知道它会有这样的副作用。谢啦!现在一切正常。
    【解决方案3】:

    另一种方法是创建HandlerMethodArgumentResolver。它很冗长,但您可以处理查询字符串,注入 ObjectMapper(保持相同的配置)并自己进行转换。

    我创建了一个注释来过滤我想要处理的类型:

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

    然后创建解析器:

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

    用法:

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

    顺便说一句,不要忘记以这种方式注册您的解析器以便能够注入 bean:

    @Configuration
    public class ArgumentResolverConfig implements WebMvcConfigurer {
        @Autowired
        private QueryStringArgumentResolver argumentResolver;
    
        @Override
        public void addArgumentResolvers(
                final List<HandlerMethodArgumentResolver> argumentResolvers) {
            argumentResolvers.add(argumentResolver);
        }
    }
    

    【讨论】:

      【解决方案4】:

      首先,您的请求参数 (some_value=1500&amp;some_other_value=50000) 映射到 RequestParam,而不是 RequestBody。 其次,jackson 库反序列化的是 RequestBody,而不是 RequestParam。 三、Post或Put http方法映射参数到body。

      所以,您正在请求 GET 方法和请求参数,而您的 jackson 配置不适用于您的请求参数。

      Camel case 参数,或@RequestMapping(name="some_param") String someParam 将映射到您的请求参数。

      【讨论】:

      • 感谢您的回复,但这不是我的问题。我已经知道那些是@RequestParams ......而不是身体......如果我收到一个GET请求,它不应该有一个身体......但我不确定你甚至想说什么.如果我传递了所有符合 POJO 的参数,那么我得到的 POJO 实例很好……某些东西(如果不是 jackson)正在做映射。并忽略我的 propertynamestrategies 配置。那是我的问题。我已经在我的回复中使用了解决方法。我不确定我理解你的意思。
      【解决方案5】:

      所以@Barath 是对的,您不会从 GET 参数中获得干净的反序列化对象。但是有一个解决方法。不确定这是否合法...但它是这样的:

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

      这样你仍然有你的 GET 方法,你把所有的参数反序列化成一个漂亮的小对象,让你做任何事情。希望这对任何人都有帮助。我希望 Spring/Jackson 的人能够像使用 POST 方法一样启用 GET 参数的自动反序列化。

      【讨论】:

        猜你喜欢
        • 2015-05-27
        • 2015-02-01
        • 2019-05-15
        • 2020-02-10
        • 1970-01-01
        • 2021-04-07
        • 1970-01-01
        • 2020-10-08
        • 2019-10-30
        相关资源
        最近更新 更多