JSON 中 Long类型精度缺失的问题

作者:Bright Xu

问题描述:后端返回的JSON字符串{"id": 130872882849357824, "name": "张三", "age": 22},前端通过user.id获取id时,id的值变成了130872882849357820,也就是说id的个位数精度缺失了。

问题原因:主要原因是前端将json字符串反序列化成对象时,将这个id转成了Number类型。JavaScriptNumber类型是IEEE 754双精度浮点型,在这个规定中能安全的表示整数的范围在 − ( 2 53 − 1 ) -(2^{53}-1) (2531) 2 53 − 1 2^{53}-1 2531 之间。超过了这个安全整数的范围,数据就不准确了。

解决思路:所以主要的解决思路是在后端将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);
        };
    }
}

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐