JSON 中 Long类型精度缺失的问题
后端传入的JSON字符串`{"id": 130872882849357824, "name": "张三", "age": 22}`,前端通过`user.id`获取`id`时,`id`的值变成了`130872882849357820`,也就是说`id`的个位数精度缺失了。主要原因是前端将json字符串反序列化成对象时,将这个`id`转成了`Number`类型。
JSON 中 Long类型精度缺失的问题
作者:Bright Xu
问题描述:后端返回的JSON字符串{"id": 130872882849357824, "name": "张三", "age": 22}
,前端通过user.id
获取id
时,id
的值变成了130872882849357820
,也就是说id
的个位数精度缺失了。
问题原因:主要原因是前端将json字符串反序列化成对象时,将这个id
转成了Number
类型。JavaScript
的Number
类型是IEEE 754
的双精度浮点型,在这个规定中能安全的表示整数的范围在
−
(
2
53
−
1
)
-(2^{53}-1)
−(253−1) 到
2
53
−
1
2^{53}-1
253−1 之间。超过了这个安全整数的范围,数据就不准确了。
解决思路:所以主要的解决思路是在后端将Long
类型的数据序列化成JSON
时,先判断数值的范围是否超过了安全整数的范围,如果超过了就将其转为字符串,如果没有超过就保持原本的类型和值。结果形如{"id": "130872882849357824", "name": "张三", "age": 22}
的JSON字符串。
以下是基于Spring Boot
的解决方式:
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.IOException;
/**
* jackson 序列化配置
*
* @author Bright Xu
* @since 2022/01/02
*/
@Configuration
public class JacksonSerializerConfig {
/**
* JavaScript 中最大的安全整数 <code>2<sup>53</sup> - 1</code>
*
* @see <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER">MDN - MAX_SAFE_INTEGER</a>
*/
public static final long MAX_SAFE_INTEGER = 0x1FFFFFFFFFFFFFL;
/**
* JavaScript 中最小的安全整数 <code>-(2<sup>53</sup> - 1)</code>
*
* @see <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Number/MIN_SAFE_INTEGER">MDN - MIN_SAFE_INTEGER</a>
*/
public static final long MIN_SAFE_INTEGER = -0x1FFFFFFFFFFFFFL;
private static final JsonSerializer<Long> SERIALIZER = new JsonSerializer<Long>() {
@Override
public void serialize(Long value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
if (value == null) {
gen.writeNull();
return;
}
// 是否是 JavaScript 安全整数
boolean isSafeInteger = MIN_SAFE_INTEGER <= value && value <= MAX_SAFE_INTEGER;
if (isSafeInteger) {
gen.writeNumber(value);
} else {
gen.writeString(value.toString());
}
}
};
/**
* 解决JavaScript在 json 反序列化时 long 类型缺失精度问题
*
* @return Jackson2ObjectMapperBuilderCustomizer
*/
@Bean
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
return jacksonObjectMapperBuilder -> {
jacksonObjectMapperBuilder.serializerByType(Long.TYPE, SERIALIZER);
jacksonObjectMapperBuilder.serializerByType(Long.class, SERIALIZER);
};
}
}
更多推荐
所有评论(0)