Spring MVC 中如何自定义 Gson 的消息转换器?

Spring MVC 中通过配置 Gson 的消息转换器可以使 Controller 层直接返回对象而非手动去转换对象为 json 字符串之后再返回。

  <mvc:annotation-driven>
    <mvc:message-converters>
      <!--避免返回String乱码-->
      <bean class="org.springframework.http.converter.StringHttpMessageConverter">
        <property name="supportedMediaTypes">
          <list>
            <value>text/plain;charset=UTF-8</value>
            <value>text/html;charset=UTF-8</value>
          </list>
        </property>
      </bean>
      <!--Json转换,property 避免 Ie 执行 Ajax 时,返回 Json 出现下载文件-->
      <bean class="org.springframework.http.converter.json.GsonHttpMessageConverter">
        <property name="supportedMediaTypes">
          <list>
            <value>text/plain;charset=UTF-8</value>
            <value>application/json;charset=UTF-8</value>
          </list>
        </property>
        <!--输出json结果格式化-->
        <property name="gson">
          <bean class="org.springframework.http.converter.json.GsonFactoryBean">
            <!--输出null,false则不输出null值
            <property name="serializeNulls" value="true" />-->
            <!--不对html标签转码,false会导致所有html标签转码为 \ue300 格式-->
            <property name="disableHtmlEscaping" value="true"/>
            <!--格式化日期-->
            <property name="dateFormatPattern" value="yyyy-MM-dd HH:mm:ss"/>
          </bean>
        </property>
      </bean>
    </mvc:message-converters>
  </mvc:annotation-driven>

Java 中的代码也很简洁,类似于下面那的代码:

  @RequestMapping(path = "/getUserInfoPageByParam")
  @ResponseBody
  public JsonResult<UserInfo> getUserInfoPageByParam() {
    //获取数据并直接返回,由 GsonHttpMessageConverter 在后台进行转换包装成 json 字符串
    return jsonResult;
  }

但是 Gson 默认的转换会把 Java 中的 long/Long 类型的数据转换为 JavaScript 中的 Number 类型,这看起来没什么问题的转换却会造成一个麻烦,就是 JavaJavaScript 的数值类型最大值不同。JavaLong 最大值在 JavaScript 会出现溢出的行为,然后就造成了大整数精度丢失。
例如 396193654462615552 到 JavaScript 中之后会变成 396193654462615550,现在吾辈只能手动转换对象,将 Long 都包装成字符串类型然后传到页面,Controller 里面是类似于下面的代码:

  @RequestMapping(path = "/getUserInfoPageByParam")
  @ResponseBody
  public String getUserInfoPageByParam() {
    //获取数据并存到 jsonResult 中
    return GsonUtil.gsonToString(jsonResult);
  }

现在这样暂时能用,但这样的代码太丑了点,有没有什么办法能够做到类似于第一种直接返回对象不用手动转换的方法呢?(前提是把 Long 的大数字溢出解决掉呀)

阅读 6.4k
4 个回答

问题前几天通过一个方法解决掉了,不过今天才来的及补充一下解决问题的方法。

其实也就是将默认使用的 GsonHttpMessageConverter 消息转换器给替换掉了
spring-web.xml 里面的相关配置如下:

 <!--消息转换器 输出对象转JSON支持-->
  <mvc:annotation-driven>
    <mvc:message-converters>
      <!--避免返回String乱码-->
      <bean class="org.springframework.http.converter.StringHttpMessageConverter">
        <property name="supportedMediaTypes">
          <list>
            <value>text/plain;charset=UTF-8</value>
            <value>text/html;charset=UTF-8</value>
          </list>
        </property>
      </bean>
      <!--Json转换,property 避免 Ie 执行 Ajax 时,返回 Json 出现下载文件-->
      <!--<bean class="org.springframework.http.converter.json.GsonHttpMessageConverter">-->
      <!--这儿是重点,吾辈将默认使用的 GsonHttpMessageConverter 替换成了吾辈自己的 GsonMessageConfig 类-->
      <bean class="com.interview.conf.GsonMessageConfig">
        <property name="supportedMediaTypes">
          <list>
            <value>text/plain;charset=UTF-8</value>
            <value>application/json;charset=UTF-8</value>
          </list>
        </property>
        <!--输出json结果格式化-->
        <property name="gson">
          <bean class="org.springframework.http.converter.json.GsonFactoryBean">
            <!--输出null,false则不输出null值
            <property name="serializeNulls" value="true" />-->
            <!--不对html标签转码,false会导致所有html标签转码为 \ue300 格式-->
            <property name="disableHtmlEscaping" value="true"/>
            <!--格式化日期-->
            <property name="dateFormatPattern" value="yyyy-MM-dd HH:mm:ss"/>
          </bean>
        </property>
      </bean>
    </mvc:message-converters>
  </mvc:annotation-driven>

下面是 GsonMessageConfig 的代码:

package com.interview.conf;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.LongSerializationPolicy;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.GsonHttpMessageConverter;

/**
 * Gson 转换时的一些问题,手动配置一下转换的对象.
 *
 * @author rxliuli
 */
@Configuration
public class GsonMessageConfig extends GsonHttpMessageConverter {
  /**
   * 项目中转换 json 默认使用的 Gson 对象.
   */
  private static final Gson DEFAULT_GSON = new GsonBuilder()
    //null 也序列化
    .serializeNulls()
    //时间转化为特定格式 yyyy-MM-dd HH:mm:ss
    .setDateFormat("yyyy-MM-dd hh:mm:ss")
    //设置 Long 类型自动转换成 String 类型
    .setLongSerializationPolicy(LongSerializationPolicy.STRING)
    //执行创建 Gson 转换对象
    .create();

  @Override
  public void setGson(Gson gson) {
    super.setGson(
      DEFAULT_GSON
    );
  }
}

这样,吾辈 web 层的代码全部都不必进行任何修改,在页面中取值时所有的 Long 类型的字段都会被转换成 String 字符串类型,不用再担心 Java 中的 Long 类型的字段到 js 时发生大数字溢出了!(不过讲道理吾辈总觉得有更好的解决方案来着,这种方式略微有些不太优雅。。。(´◞⊖◟`))

直接用注解

@RestController
public class HelloWorldController {

    @RequestMapping("/hello")
    public Test test() {
        Test test =  new Test();
        test.setId(1);
        test.setName("哈哈哈")
        return test;
    }

} 

这样的返回就是 json

<mvc:annotation-driven/> 默认就提供了:数据绑定支持,@NumberFormatannotation支持,@DateTimeFormat支持,@Valid支持,读写XML的支持(JAXB,读写JSON的支持(Jackson)。我们处理响应ajax请求时,就使用到了对json的支持(配置之后,在加入了jackson的core和mapper包之后,不写配置文件也能自动转换成json)。
你配置Gson,使 Controller 层直接返回对象而非手动去转换对象为 json 字符串之后再返回的做法其实算是多此一举。

直接这样配置就够了:

<mvc:annotation-driven/>

Gson支持自定义类型转换

GsonBuilder builder=new GsonBuilder();
builder.setDateFormat("yyyy-MM-dd kk:mm:ss");
builder.registerTypeAdapter(Long.class, new Test1());
gson=builder.create(); 

class Test1 extends TypeAdapter<Long>{

    @Override
    public Long read(JsonReader arg0) throws IOException {
        // TODO Auto-generated method stub
        return null;
    }
    @Override
    public void write(JsonWriter arg0, Long arg1) throws IOException {
        // TODO Auto-generated method stub
        
    }
}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题