java的long类型转为json格式后,js中精度丢失问题

本文介绍了解决Java中long类型数据在转化为JSON格式后,JavaScript端显示精度丢失的问题。提供两种解决方案:一是数据层转换,自定义RowMapper将bigint转为string;二是配置Spring MVC的消息转换器使用自定义ObjectMapper。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

java的long类型转为json格式后,js中精度丢失问题

环境:

使用spring mvc 配置json消息转换器为MappingJackson2HttpMessageConverter

发现long类型的数据到了js端会丢失精度

解决方案:

将long统一转为string类型

方法一:

数据层转换,由于项目使用了spring 的jdbc模版类,查询时调用了spring的query方法

    public <T> List<T> query(String sql, SqlParameterSource paramSource, RowMapper<T> rowMapper)
            throws DataAccessException {

        return getJdbcOperations().query(getPreparedStatementCreator(sql, paramSource), rowMapper);
    }

这个方法有个参数rowMapper,我用来将数据库数据转换为Map,我通过自定义实现RowMapper接口,将mysql数据库里的bigint类型转为string而不是默认的long,代码如下:

复制代码
public class HashMapRowMapper implements RowMapper<Map<String, Object>> {

    @Override
    public Map<String, Object> mapRow(ResultSet rs, int rowNum) throws SQLException {
        ResultSetMetaData rsmd = rs.getMetaData();
        int columnCount = rsmd.getColumnCount();
        Map<String, Object> mapOfColValues = new LinkedCaseInsensitiveMap<Object>(columnCount);
        for (int i = 1; i <= columnCount; i++) {
            String key = JdbcUtils.lookupColumnName(rsmd, i);
            Object obj = JdbcUtils.getResultSetValue(rs, i);
            if (obj == null) {
                mapOfColValues.put(key, obj);
            }
            if (obj != null) {
                Class<?> cc = obj.getClass();
                if (cc.getName().equals("java.math.BigInteger") && obj != null) {
                    mapOfColValues.put(key, String.valueOf(obj));
                } else {
                    mapOfColValues.put(key, obj);
                }
            }
        }
        return mapOfColValues;
    }

}
复制代码

调用代码如下,注意query传入的最后一个参数使用了自定已类HashMapRowMapper:

复制代码
    public Map<String, Object> getItem(Map<String, Object> inParam) {
        if (MapUtil.getValue("id", inParam) == "")
            throw new InParamCheckException("id不能为空:" + inParam.get("id"));
        String orgId = MapUtil.getValue("org_id", inParam);
        String appId = IDConstants.APP_ID_PIS;
        NamedParameterJdbcTemplate jdbc = jdbcRouteHelper.getJDBCTemplate(orgId, appId);

        return new MyMap().put("simple_info", jdbc.query("select * from simple_info where id=:id", inParam, new HashMapRowMapper()))
                .put("simple_test",
                        jdbc.query("select * from simple_test where simple_info_id=:id", inParam, new HashMapRowMapper()))
                .getMap();
    }
复制代码

 

 

方法二:

配置spring的消息转换器的ObjectMapper为自定义的类

配置如下:

复制代码
    <bean
        class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
        <property name="messageConverters">
            <list>
                <bean
                    class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                    <property name="objectMapper">
                        <bean class="LongToStringJsonConverter">
                            <property name="supportedMediaTypes">
                                <list>
                                    <value>application/json;charset=UTF-8</value>
                                    <value>text/html;charset=UTF-8</value>
                                    <value>text/plain;charset=UTF-8</value>
                                </list>
                            </property>
                            <property name="dateFormat">
                                <bean class="java.text.SimpleDateFormat">
                                    <constructor-arg type="java.lang.String" value="yyyy-MM-dd HH:mm:ss" />
                                </bean>
                            </property>
                        </bean>
                    </property>
                </bean>
    </bean>
复制代码

其中LongToStringJsonConverter为自定义转换器

复制代码
public class LongToStringJsonConverter extends ObjectMapper {
    /**
     * 
     */
    private static final long serialVersionUID = 1683531771040674386L;

    @Override
    public ObjectMapper registerModule(Module module) {
        SimpleModule simpleModule = new SimpleModule();
        simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
        simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
        return super.registerModule(simpleModule);
    }
}
复制代码

spring中controller层的代码使用时无需特别修改,下面是一个controller类里返回json数据,代码不做任何修改,long类型已经转为string返回到web浏览器了.

复制代码
    @RequestMapping("/")
    @ResponseBody
    public Map<String, Object> init() {
        List<Object> lstDoctor = dictConsumer.getBaseParam().getItemsFromCache(cache_keys.PIS_doctor.name(),
                test_org_id, IDConstants.APP_ID_PIS);
        List<Object> lstSubjectType = dictConsumer.getBaseParam().getItemsFromCache(cache_keys.PIS_subject_type.name(),
                test_org_id, IDConstants.APP_ID_PIS);
        List<Object> lstTest = dictConsumer.getBaseParam().getItemsFromCache(cache_keys.PIS_test.name(),
                test_org_id, IDConstants.APP_ID_PIS);        
        MyMap map = new MyMap();
        map.put("doctors", lstDoctor).put("subject_types", lstSubjectType).put("tests", lstTest);
        return map.getMap();

    }
复制代码

 

### Java前后端Long类型精度丢失解决方案 在Java与前端交互过程中,由于JavaScript的`Number`类型的数值范围有限(约为±(2^53)-1),而Java的`Long`类型可以表示更大的整数范围(约±(2^63)-1)。因此,在前后端传递大数值时容易发生精度丢失问题。 以下是几种常见的解决方案: #### 方案一:后端将Long类型转为String类型发送到前端 通过修改后端代码,将`Long`类型的字段序列化为字符串形式后再传递至前端。这样可以有效避免因数值过大而导致的精度丢失问题[^1]。 ```java import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; @Data @TableName("ap_article") public class ApArticle implements Serializable { @TableId(value = "id", type = IdType.ID_WORKER) @JsonSerialize(using = ToStringSerializer.class) // 将字段换为字符串进行响应 private Long id; } ``` 此方法利用Jackson库中的`ToStringSerializer`实现自动换功能[^5]。 --- #### 方案二:前端使用BigInt替代Number类型 对于现代浏览器环境下的应用开发,可以通过让前端采用`BigInt`来代替传统的`Number`类型处理大数据量场景下可能出现的问题[^4]。需要注意的是该特性仅适用于支持ES2020及以上版本标准的新一代Web客户端程序设计框架之中。 例如: ```javascript let bigIntValue = BigInt('1435421253099634623'); console.log(bigIntValue.toString()); // 输出完整的原始值 ``` 这种方式要求服务端仍然保持原有的数据结构不变,只是调整了客户端解析逻辑[^2]。 --- #### 方案三:统一约定并限制ID长度 如果项目允许的话,也可以考虑重新规划业务模型中的唯一标识符生成策略,比如缩短其位宽或者选用其他更适合跨平台共享的数据格式作为主键定义依据之一[^3]。不过这种方法通常会带来额外的工作负担以及潜在的风险因素,故需谨慎评估实施成本效益比之后再做决定。 --- #### 总结 以上三种方式各有优劣之处,具体选择哪一种取决于实际应用场景需求和技术栈现状等因素综合考量结果得出结论。一般推荐优先尝试第一种做法即借助JSON序列化工具包完成自动化映射操作最为简便高效;而对于那些追求极致性能优化或者是老旧系统升级改造工程,则可能需要结合第二种甚至第三种手段共同作用才能达到理想效果。 ```python # 示例Python伪代码展示如何验证上述理论正确性的简单测试脚本 def test_long_precision(): large_number_str = '1435421253099634623' try: # JavaScript Number 类型无法精确存储这个数字 number_value = float(large_number_str) print(f'Float representation: {number_value}') # 使用 Python 的 int 或者 str 来保留原样 exact_representation_via_int = int(large_number_str) print(f'Exact via Int: {exact_representation_via_int}') exact_representation_via_string = large_number_str print(f'Exact via String: "{exact_representation_via_string}"') except Exception as e: print(e) test_long_precision() ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值