在Spring Boot中使用Gson配置snake_case查询参数

时间:2017-03-31 17:00:26

标签: spring-boot gson

我尝试将Gson配置为我的JSON映射器以接受“snake_case”查询参数,并将它们转换为标准Java“camelCase”参数。

首先,我知道我可以使用@SerializedName注释来自定义每个字段的序列化名称,但这将涉及一些手动工作。

在做了一些搜索之后,我相信以下方法应该有效(如果我错了,请纠正我。)

  1. 使用Gson作为Spring Boot的默认JSON映射器
  2. spring.http.converters.preferred-json-mapper=gson

    1. 在创建GsonHttpMessageConverter之前配置Gson as described here

    2. 根据GSON Field Naming Policy

    3. 在步骤2中自定义Gson命名政策
      private GsonHttpMessageConverter createGsonHttpMessageConverter() {
          Gson gson = new GsonBuilder()
                         .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
                         .create();
      
          GsonHttpMessageConverter gsonConverter = new GsonHttpMessageConverter();
          gsonConverter.setGson(gson);
      
          return gsonConverter;
      }
      

      然后我创建一个这样的简单控制器:

      @RequestMapping(value = "/example/gson-naming-policy")
      public Object testNamingPolicy(ExampleParam data) {
          return data.getCamelCase();
      }
      

      使用以下Param类:

      import lombok.Data;
      
      @Data
      public class ExampleParam {
      
          private String camelCase;
      
      }
      

      但是当我使用查询参数?camel_case=hello调用控制器时,data.camelCase无法填充(并且它为空)。当我将查询参数更改为?camelCase=hello时,可以设置它,这意味着我的设置无法按预期工作。

      任何提示都将受到高度赞赏。提前谢谢!

1 个答案:

答案 0 :(得分:1)

这是一个很好的问题。如果我理解Spring MVC如何在幕后工作,那么@ModelAttribute驱动的HTTP转换器就不会被使用。从ExampleParam构造函数或ExampleParam.setCamelCase方法(首先是de Lombok)抛出异常时可以很容易地检查它 - Spring使用它的bean实用程序使用public(!){{ 1}}设置DTO值。另一个证据是,无论您的Gson转换器配置如何,都不会调用ExampleParam.setCamelCase。所以,你的Gson.fromJson会让你感到困惑,因为默认的Gson实例和Spring一样使用这个策略 - 所以这只是一个混乱的问题。

为了使其工作,您必须创建一个自定义的Gson感知camelCase实现。假设我们只支持POJO(不是列表,地图或基元)。

HandlerMethodArgumentResolver

所以,结果如下:

  • @Configuration @EnableWebMvc class WebMvcConfiguration extends WebMvcConfigurerAdapter { private static final Gson gson = new GsonBuilder() .setFieldNamingPolicy(LOWER_CASE_WITH_UNDERSCORES) .create(); @Override public void addArgumentResolvers(final List<HandlerMethodArgumentResolver> argumentResolvers) { argumentResolvers.add(new HandlerMethodArgumentResolver() { @Override public boolean supportsParameter(final MethodParameter parameter) { // It must be never a primitive, array, string, boxed number, map or list -- and whatever you configure ;) final Class<?> parameterType = parameter.getParameterType(); return !parameterType.isPrimitive() && !parameterType.isArray() && parameterType != String.class && !Number.class.isAssignableFrom(parameterType) && !Map.class.isAssignableFrom(parameterType) && !List.class.isAssignableFrom(parameterType); } @Override public Object resolveArgument(final MethodParameter parameter, final ModelAndViewContainer mavContainer, final NativeWebRequest webRequest, final WebDataBinderFactory binderFactory) { // Now we're deconstructing the request parameters creating a JSON tree, because Gson can convert from JSON trees to POJOs transparently // Also note parameter.getGenericParameterType() -- it's better that Class<?> that cannot hold generic types parameterization return gson.fromJson( parameterMapToJsonElement(webRequest.getParameterMap()), parameter.getGenericParameterType() ); } }); } ... private static JsonElement parameterMapToJsonElement(final Map<String, String[]> parameters) { final JsonObject jsonObject = new JsonObject(); for ( final Entry<String, String[]> e : parameters.entrySet() ) { final String key = e.getKey(); final String[] value = e.getValue(); final JsonElement jsonValue; switch ( value.length ) { case 0: // As far as I understand, this must never happen, but I'm not sure jsonValue = JsonNull.INSTANCE; break; case 1: // If there's a single value only, let's convert it to a string literal // Gson is good at "weak typing": strings can be parsed automatically to numbers and booleans jsonValue = new JsonPrimitive(value[0]); break; default: // If there are more than 1 element -- make it an array final JsonArray jsonArray = new JsonArray(); for ( int i = 0; i < value.length; i++ ) { jsonArray.add(value[i]); } jsonValue = jsonArray; break; } jsonObject.add(key, jsonValue); } return jsonObject; } } =&gt; (空)
  • http://localhost:8080/?camelCase=hello =&gt; http://localhost:8080/?camel_case=hello