JSON 序列化时,数字怎么偷偷‘变身’了?Jackson 科学计数法触发机制全解

522 阅读3分钟

大家好,我是G探险者!

在使用 Jackson 进行 JSON 序列化和反序列化时,我们有时会遇到数字被以 科学计数法(Scientific Notation) 形式表示的问题。这种表现形式在大多数计算场景下无伤大雅,但在涉及精度敏感、报文协议或前端展示时,往往会带来不小的困扰。

本文将详细探讨:

  • ✅ 什么是科学计数法?
  • ✅ Jackson 在什么情况下会触发科学计数法?
  • ✅ 如何通过配置规避 Jackson 中的科学计数法?
  • ✅ 常见的坑与对策

📌 什么是科学计数法?

科学计数法是一种用指数形式表达非常大或非常小的数字的方法。其形式通常为:

1.23E5 => 1.23 × 10^5 => 123000

Java 中的 doublefloat 类型在打印或序列化时,默认就可能使用科学计数法。


🎯 Jackson 触发科学计数法的典型条件

在使用 Jackson 处理数字时,是否以科学计数法表示,取决于以下几方面:

✅ 1. 数据类型决定表现形式

数据类型是否易触发科学计数法说明
double / Double✅ 高Java 默认使用科学计数法表示极大或极小的值
float / Float✅ 高精度更低,更容易触发科学计数法
BigDecimal❌ 低精度高,Jackson 默认不会使用科学计数法(除非关闭某些配置)

✅ 2. 数值大小触发阈值(以 double 为例)

数值范围是否可能触发科学计数法
< 0.001(1E-3 以下)✅ 可能,例如 0.00000011E-7
>= 10000000(1E7 以上)✅ 可能,例如 12345678.91.23456789E7
正常整数、小数(两位数)❌ 不会,例如 123.4512.34

这是一种行为优化,Jackson 会尝试在 JSON 序列化时生成更短的字符串。


✅ 3. 示例:Jackson 默认行为

ObjectMapper mapper = new ObjectMapper();

System.out.println(mapper.writeValueAsString(12345.67));       
// 输出: 12345.67 ✅

System.out.println(mapper.writeValueAsString(12345678.9));     
// 输出: 1.23456789E7 ❌

System.out.println(mapper.writeValueAsString(0.000123));       
// 输出: 1.23E-4 ❌


🧪 BigDecimal 是如何处理的?

BigDecimal 是精度友好的数值类型,Jackson 默认不会用科学计数法。但当关闭了相关配置或位数特别长时,也有可能被转为科学计数法。

✅ 示例

BigDecimal value = new BigDecimal("1234567890123456789012345678901234");
System.out.println(mapper.writeValueAsString(value));
// 默认输出:1234567890123456789012345678901234 ✅

❌ 关闭配置后的行为

mapper.configure(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN, false);

可能导致输出为:

1.2345678901234568E33 ❌


🛠 如何规避 Jackson 的科学计数法?

✅ 方式 1:反序列化时启用 BigDecimal

mapper.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);

避免将浮点数反序列化为 double,从而防止精度丢失和科学计数法。


✅ 方式 2:序列化时强制以普通字符串输出 BigDecimal

mapper.configure(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN, true);

确保 Jackson 在序列化时调用 BigDecimal.toPlainString() 而不是 toEngineeringString()toString()


✅ 方式 3:统一使用字符串表示数值(如账务金额)

在精度极其重要的场景中,如金融、电商等,可以直接用字符串保存数值:

@JsonSerialize(using = ToStringSerializer.class)
private BigDecimal amount;


🚧 实战坑点总结

问题原因解决方案
前端拿到 1.23E8 无法解析Jackson 默认输出科学计数法配置 WRITE_BIGDECIMAL_AS_PLAIN
精度丢失,金额计算出错double 存储金额使用 BigDecimal
科学计数法序列化后值变长BigDecimal.toString() 被默认调用强制使用 .toPlainString()

✅ 结语

Jackson 并不会“自动触发”科学计数法,而是在数值超出正常范围后,出于效率和表达优化的考虑,采用了科学记数的格式。开发者应在:

  • 💰 金融业务;
  • 🧮 精度敏感场景;
  • 📤 前后端数值交互场景;

中对科学计数法的触发条件与规避方式有足够了解,才能保障系统的稳定性和准确性。