JSON序列化导致Long类型被搞成Integer怎么解决

发布时间:2022-01-13 17:54:48 作者:iii
来源:亿速云 阅读:717
# JSON序列化导致Long类型被搞成Integer怎么解决

## 引言

在Java开发中,JSON序列化/反序列化是系统间数据交互的常见操作。但当处理数值类型时,开发者常会遇到一个棘手问题:明明定义的`Long`类型字段,经过JSON序列化后却变成了`Integer`,导致数据精度丢失或类型转换异常。本文将深入分析问题根源,并提供5种实用解决方案。

---

## 问题现象与复现

### 典型报错场景
```java
// 定义DTO对象
public class OrderDTO {
    private Long orderId;  // 数据库主键通常为Long
    // getters/setters...
}

// 序列化测试
OrderDTO order = new OrderDTO();
order.setOrderId(123456789012345L); // 15位长整型

String json = JSON.toJSONString(order); // 使用Fastjson等库序列化
OrderDTO parsed = JSON.parseObject(json, OrderDTO.class);

System.out.println(parsed.getOrderId().getClass()); // 输出可能是java.lang.Integer!

为什么会出现这种情况?

  1. JSON规范限制:JSON本身没有整数/长整型的区分,所有数字都被视为Number
  2. 库的自动类型推断:为了”优化”,某些库会将小范围数字判定为Integer
  3. 科学计数法问题:大数字可能被转为科学计数法表示

根本原因分析

1. 序列化库的实现差异

库名称 默认行为 配置方式
Fastjson 根据数值范围自动转换 SerializerFeature
Jackson 相对保守,但也会类型推断 JsonGenerator配置
Gson 完全依赖运行时类型 无需特殊配置

2. Java类型擦除的影响

泛型在运行时被擦除,反序列化时可能丢失原始类型信息:

List<Long> ids = Arrays.asList(100L, 200L);
// 序列化后可能变成[100,200],反序列化时无法确定是Long

解决方案大全

方案1:强制声明类型(推荐)

适用场景:所有主流JSON库

Fastjson配置

// 序列化时保留类型
String json = JSON.toJSONString(order, 
    SerializerFeature.WriteClassName);

// 反序列化时识别类型
OrderDTO parsed = JSON.parseObject(json, OrderDTO.class, 
    Feature.SupportAutoType);

Jackson配置

ObjectMapper mapper = new ObjectMapper();
mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, 
    ObjectMapper.DefaultTyping.NON_FINAL);

方案2:自定义序列化器

public class LongToStringSerializer extends JsonSerializer<Long> {
    @Override
    public void serialize(Long value, JsonGenerator gen, 
        SerializerProvider provider) throws IOException {
        gen.writeString(value.toString()); // 转为字符串传输
    }
}

// 注册注解
public class OrderDTO {
    @JsonSerialize(using = LongToStringSerializer.class)
    private Long orderId;
}

方案3:全局类型配置

Jackson全局配置示例

ObjectMapper mapper = new ObjectMapper();
mapper.configOverride(Long.class)
    .setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.STRING));

方案4:使用第三方工具类

// 使用Hutool工具包
String json = JSONUtil.toJsonStr(order);
OrderDTO parsed = JSONUtil.toBean(json, OrderDTO.class);

方案5:DTO层做类型转换

public class OrderDTO {
    private String orderId; // 前端传输用String
    
    public Long getOrderIdAsLong() {
        return Long.parseLong(orderId);
    }
}

各方案对比评估

方案 可靠性 性能影响 侵入性 适用场景
强制声明类型 ★★★★★ 较小 通用方案
自定义序列化器 ★★★★ 中等 需要精确控制
全局配置 ★★★★ 新项目
工具类 ★★★ 较大 快速解决
DTO转换 ★★ 老旧系统

最佳实践建议

  1. 前后端统一规范

    • 定义id字段统一用String传输
    • 文档中明确数字类型的精度要求
  2. 监控与防御编程

// 在反序列化后添加校验
if (parsed.getOrderId() instanceof Integer) {
    throw new IllegalStateException("Type conversion error!");
}
  1. 性能优化技巧
    • 对于高并发场景,提前创建ObjectMapper实例并缓存
    • 避免在循环中重复创建JSON解析器

常见问题FAQ

Q:为什么数据库的BIGINT映射到Java的Long还会出问题?
A:问题出在JSON这一传输层,与数据库无关

Q:使用String存储Long有什么缺点?
A:会增加约30%的存储空间,但现代压缩算法可缓解

Q:如何批量处理历史数据?
A:推荐使用jq命令行工具预处理:

cat old.json | jq '.id |= tostring' > new.json

总结

JSON序列化中的Long类型问题本质是类型系统边界问题。通过本文介绍的5种方案,开发者可以根据实际场景选择最适合的解决方式。建议新项目直接采用方案1+方案3的组合,既保证类型安全又不失灵活性。

关键点:在分布式系统中,显式类型声明永远比隐式转换更可靠 “`

推荐阅读:
  1. 数据类型 char,int,short,long long,id
  2. java怎么判断是否是Long类型

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

json integer long

上一篇:Qt怎么模仿Visual Studio停靠窗口效果

下一篇:springboot整合quartz定时任务框架的方法是什么

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》