Long类型参数传到前端精度丢失的解决方案

一、前言

由于公司数据库表的id是利用雪花算法生成的,所以实体类里面定义的数据类型为Long。但是这个数据传到前端时,发生了精度丢失的现象。本文记录了从java后端的角度如何解决这个精度丢失的问题,便于自己后续查阅。

二、问题的描述

前端通过ajax请求后端接口,返回json数据,然后将数据渲染到一个表格中。突然发现表格中id这一列出现了精度丢失的现象,这精度丢失是由前端引起的。

三、解决方案

1. 提出方案

在后端代码中将Long类型改为String类型即可,但是由于采用的SpringMVC框架,可简单的做到统一处理。在输出到页面的Json转换器里面做统一处理,对象序列化成json时,将Long类型变成String类型就可以了。

2. 查询资料

由于公司里面用的是FastJson消息转换器,因此我们只需要扩展下FastJsonHttpMessageConverter这个类。 由于我们需要在fastJson将对象序列化成Json时做处理,因此先了解下FastJson的SerializeFilter接口。该接口有好多子接口,不同类型的子接口的作用是不同的,具体如下:

SerializeFilter :是通过编程扩展的方式定制序列化。
fastjson支持6种SerializeFilter,用于不同场景的定制序列化。具体如下:

1. PropertyPreFilter 根据PropertyName判断是否序列化
2. PropertyFilter 根据PropertyName和PropertyValue来判断是否序列化
3. NameFilter 修改Key,如果需要修改Key,process返回值则可
4. ValueFilter 修改Value
5. BeforeFilter 序列化时在最前添加内容
6. AfterFilter 序列化时在最后添加内容

3. 实战代码

(1). 自定义序列化Filter

由于我们需要在对象序列化成json的阶段,把Long类型的Value变成String类型,因此先写一个ValueFilter的实现类。在这实现类里面,我们把数据类型为Long的数据的Value给修改成String类型。具体代码如下:

package com.kangxiinfo.wechat.common.fastjson;

import com.alibaba.fastjson.serializer.ValueFilter;
/**
 * Long类型变成String类型——json序列化Filter
 * @author ZENG.XIAO.YAN
 * @time   2018-11-08 14:50:19
 * @version  v1.0
 */
public class LongToStringSerializeFilter implements ValueFilter {
    @Override
    public Object process(Object object, String name, Object value) {
        if (value != null) {
            // 当value不为null时,如果value是Long类型的就改成String
            if (value instanceof Long) {
                return value.toString();
            }
        }
        return value;
    }   
}

(2). 自定义JsonHttpMessageConverter

直接继承FastJsonHttpMessageConverter这个类,然后重写writeInternal这个方法。在writeInternal方法里面进行对象序列成json串时,传入我们上面自定义的序列化Filter。具体代码如下:

package com.kangxiinfo.wechat.common.fastjson;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.converter.HttpMessageNotWritableException;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;

public class CustomFastJsonHttpMessageConverter extends FastJsonHttpMessageConverter {
    public final static Charset UTF8 = Charset.forName("UTF-8");
    private Charset charset = UTF8;
    private SerializerFeature[] features = new SerializerFeature[0];

    /** 自定义的filter */
    private LongToStringSerializeFilter longToStringSerializeFilter = new LongToStringSerializeFilter();

    public CustomFastJsonHttpMessageConverter() {
        super();
    }
    @Override
    protected boolean supports(Class<?> clazz) {
        return true;
    }
    public Charset getCharset() {
        return this.charset;
    }
    public void setCharset(Charset charset) {
        this.charset = charset;
    }
    public SerializerFeature[] getFeatures() {
        return features;
    }
    public void setFeatures(SerializerFeature... features) {
        this.features = features;
    }    
    // 重写这个方法
    @Override
    protected void writeInternal(Object obj, HttpOutputMessage outputMessage)
            throws IOException, HttpMessageNotWritableException {
        OutputStream out = outputMessage.getBody();
        // 转json时 加入自定义的Filter
        String text = JSON.toJSONString(obj, longToStringSerializeFilter, features);
        byte[] bytes = text.getBytes(charset);
        out.write(bytes);
    }
}

3. 在SpringMVC配置文件中配置使用我们自定义的Json转换器

配置的具体代码如下:

 <mvc:annotation-driven>
        <mvc:message-converters register-defaults="false">
            <!-- 注册我们扩展了的fastjson转换器 -->
            <bean class="com.kangxiinfo.wechat.common.fastjson.CustomFastJsonHttpMessageConverter">
                <property name="supportedMediaTypes">
                    <list>
                        <value>text/html;charset=UTF-8</value>
                        <value>application/json;charset=UTF-8</value>
                        <value>text/plain;charset=UTF-8</value>
                        <value>application/x-www-form-urlencoded;charset=UTF-8</value>                        
                    </list>
                </property>
                <property name="features">
                    <list>
                        <value>WriteMapNullValue</value>
                        <value>WriteNullListAsEmpty</value>
                        <value>WriteNullStringAsEmpty</value>
                        <value>WriteNullNumberAsZero</value>
                    </list>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

四、小结

(1).Long类型数据传到前端精度丢失时,可以将Long类型转成String类型再输出到前端就不会丢失精度
(2).可以通过扩展SpringMVC的消息转换器,全局处理这种Long类型数据精度丢失的问题

你可能感兴趣的:(【59】Java)