JavaScript处理Long型丢失精度的几种后端(Java)解决方案

概述

项目中用雪花算法生成分布式ID,这些ID是Long型的。

当这些ID在前端通过JavaScript处理时,会存在精度丢失问题。

本文不过多讨论为什么会丢失精度。

本文只以后端(Java端)角度,提供几种解决方案。

整体说明

后台将Long转换成String类型有两大类解决方案:

  • 全局处理:通过编写全局转换器,对查询结果中所有的Long都进行转换。
    • 优点:对于后端来说,开发成本很低。
    • 缺点:所有Long型字段都会被转换,可能导致前端的代码逻辑。
  • 局部处理:编写序列号和反序列化器,通过@JsonSerialize和@JsonDeserialize指定需要转换的字段。
    • 优点:对于后端来说,需要针对每一个需要转换的字段进行注解,开发成本较高,
    • 缺点:只转换指定的字段,对前端代码逻辑影响较小。

全局处理

根据当前项目类型,分为三种方式。

Spring MVC

添加配置类,自定义的配置类提供的转换器会替代默认的转换器。

/**
 * <p>Description: 自定义json转换器,取代默认的转换器</p>
 * @author hanchao
 * @date 2018/7/23 下午4:31
 */
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {

    /**
    * <p>Description: 解决js处理long型丢失精度问题,将long转换成long</p>
    * @author hanchao
    * @date 2018/7/23 下午4:31
    */
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        //定义Json转换器
        MappingJackson2HttpMessageConverter jackson2HttpMessageConverter =
                new MappingJackson2HttpMessageConverter();
        //定义对象映射器
        ObjectMapper objectMapper = new ObjectMapper();
        //定义对象模型
        SimpleModule simpleModule = new SimpleModule();
        //添加对长整型的转换关系
        simpleModule.addSerializer(BigInteger.class, ToStringSerializer.instance);
        simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
        simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
        //将对象模型添加至对象映射器
        objectMapper.registerModule(simpleModule);
        //将对象映射器添加至Json转换器
        jackson2HttpMessageConverter.setObjectMapper(objectMapper);

        //在转换器列表中添加自定义的Json转换器
        converters.add(jackson2HttpMessageConverter);
        //添加utf-8的默认String转换器
        converters.add(new StringHttpMessageConverter(Charset.forName("UTF-8")));
    }
}

Spring Boot

其实Spring Boot的修改方式与Spring MVC一致,只是由于项目主类本身就继承了WebMvcConfigurerAdapter。所以不需要自定义配置类,直接修改主类,添加@Override方法configureMessageConverters:

	/**
    * <p>Description: 解决js处理long型丢失精度问题,将long转换成long</p>
    * @author hanchao
    * @date 2018/7/23 下午4:31
    */
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        //定义Json转换器
        MappingJackson2HttpMessageConverter jackson2HttpMessageConverter =
                new MappingJackson2HttpMessageConverter();
        //定义对象映射器
        ObjectMapper objectMapper = new ObjectMapper();
        //定义对象模型
        SimpleModule simpleModule = new SimpleModule();
        //添加对长整型的转换关系
        simpleModule.addSerializer(BigInteger.class, ToStringSerializer.instance);
        simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
        simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
        //将对象模型添加至对象映射器
        objectMapper.registerModule(simpleModule);
        //将对象映射器添加至Json转换器
        jackson2HttpMessageConverter.setObjectMapper(objectMapper);

        //在转换器列表中添加自定义的Json转换器
        converters.add(jackson2HttpMessageConverter);
        //添加utf-8的默认String转换器
        converters.add(new StringHttpMessageConverter(Charset.forName("UTF-8")));
    }

没有Spring MVC

首先,定义一个由Long转换成String的工具方法:

	/**
    * <p>Description: 将long转换成String</p>
    * @author hanchao
    * @date 2018/7/23 下午5:07
    */
    public static <T> T longToString(T body,String... field) throws IOException {
        //定义对象映射器
        ObjectMapper objectMapper = new ObjectMapper();
        //定义对象模型
        SimpleModule simpleModule = new SimpleModule();
        //添加对长整型的转换关系
        simpleModule.addSerializer(BigInteger.class, ToStringSerializer.instance);
        simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
        simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
        //将对象模型添加至对象映射器
        objectMapper.registerModule(simpleModule);
        //将查询结果转换成Json字符串
        String json = objectMapper.writeValueAsString(body);
        //将Json字符串转换成查询结果
        return (T) objectMapper.readValue(json,body.getClass());
    }

然后,将查询结果在返回前端之前进行转换,具体代码就不多贴了。

局部处理

局部处理通过注解解决,无需关系项目类型。

首先,编写序列号和反序列化器:

package pers.hanchao.demo.util;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;

import java.io.IOException;

/**
 * <p>Description: 类型字段序列化时转为字符串,避免js丢失精度</P>
 *
 * @author hanchao
 * @date 2018/7/23 下午5:34
 */
public class LongJsonSerializer extends JsonSerializer<Long> {
    @Override
    public void serialize(Long value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
        String text = (value == null ? null : String.valueOf(value));
        if (text != null) {
            jsonGenerator.writeString(text);
        }
    }
}

package pers.hanchao.demo.util;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import org.apache.log4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;

/**
 * <p>Description: 将字符串转为Long</P>
 *
 * @author hanchao
 * @date 2018/7/23 下午5:35
 */
public class LongJsonDeserializer extends JsonDeserializer<Long> {
    private static final Logger logger = (Logger) LoggerFactory.getLogger(LongJsonDeserializer.class);

    @Override
    public Long deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
        String value = jsonParser.getText();
        try {
            return value == null ? null : Long.parseLong(value);
        } catch (NumberFormatException e) {
            logger.error("解析长整形错误", e);
            return null;
        }
    }
}

然后,在需要进行转换的字段上,添加注解:

    @JsonSerialize(using = LongJsonSerializer.class)
    @JsonDeserialize(using = LongJsonDeserializer.class)
    private Long id;
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值