0
点赞
收藏
分享

微信扫一扫

Spring Boot 处理 Long 类型的 json 数据,前端调用精度丢失,变成 00

惠特曼 2022-06-09 阅读 95

问题描述

错误的ID,最后两位数都是0,即:
1152921504735848700,
但是,正确的是:
1152921504735848759,

遇到Long类型的数据,传送给前端遇到精度丢失的问题.

后端的订单号值比如1089713253179727872 传给前端 显示出来是1089713253179727900。

拿postman测试接口没有问题,查到的是正确的数据,原来是因为Long类型数据传给前端可能精度丢失,js中的数据类型不能包含所有的java long值。

这个问题真是在前后端连接的夹缝中求生存了,同事跟我说拿返回的数据查不到,我拿postman调后端又完全发现不了问题。话又说回来,用swagger可以发现这个问题的。

后端(Java)数据获取正常,前端显示精度有误。

原因分析

Java Long的范围:
long的最大值:9223372036854775807
long的最小值:-9223372036854775808

大整数的精度丢失和浮点数本质上是一样的,尾数位最大是 52 位,因此 JS 中能精准表示的最大整数是 Math.pow(2, 53),十进制即 9007199254740992。

600040076242144000 > 9007199254740992

9007199254740992

大于 9007199254740992 的会丢失精度。

9007199254740992 >> 10000000000000…000 // 共计 53 个 0
9007199254740992 + 1 >> 10000000000000…001 // 中间 52 个 0
9007199254740992 + 2 >> 10000000000000…010 // 中间 51 个 0

实际上

9007199254740992 + 1 // 丢失
9007199254740992 + 2 // 未丢失
9007199254740992 + 3 // 丢失
9007199254740992 + 4 // 未丢失

解决方案:

本质上是因为long类型在转换中失真了,所以改成String类型的传就没有问题了。

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import java.util.List;

@EnableWebMvc
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
/**
* 序列换成json时,将所有的long变成string
* 因为js中得数字类型不能包含所有的java long值
*/
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
objectMapper.registerModule(simpleModule);
jackson2HttpMessageConverter.setObjectMapper(objectMapper);
converters.add(jackson2HttpMessageConverter);
}
}

另外,springboot项目可以添加注解:@JsonSerialize(using = ToStringSerializer.class).

利用JsonSerializer完成注解,也可解决Long类型精度问题:

继承JsonSerializer类

com.fasterxml.jackson.databind.JsonSerializer;
public class JsonLongSerializer extends JsonSerializer<Long> {
@Override
public void serialize(Long aLong, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeString(Long.toString(aLong));
}
}

找到VO类,在Long类型字段上面添加注解

@JsonSerialize(using = JsonLongSerializer.class )
private Long voucherId = null;

如果后台的long类型的最大值大于js的number类型,就要使用这个注解:

​@JsonSerialize(using= ToStringSerializer.class)​​ .

涉及到ID,数字的参数,后端还是发字符串类型,比较保险。


举报

相关推荐

0 条评论