Jackson 序列化 BigDecimal 丢失尾部零是因为默认调用 toString(),解决方法是用 @JsonFormat(shape = JsonFormat.Shape.STRING) 或全局注册使用 toPlainString() 的自定义序列化器,并避免转 double,前后端应约定金额字段以字符串传输。

Java 中 BigDecimal 在 Jackson 序列化为 JSON 时丢失尾部零(如 12.30 变成 12.3),是因为 Jackson 默认使用 toString() 方法序列化,而该方法返回的是“规范形式”(即去除无意义的尾随零)。要保留原始精度和尾部零,需控制序列化行为。
使用 @JsonFormat 注解指定序列化格式
在字段或 getter 方法上添加 @JsonFormat,配合自定义序列化器或指定 shape,但注意:Jackson 原生 @JsonFormat 对 BigDecimal 的 shape = JsonFormat.Shape.STRING 是最直接有效的方式——它强制以字符串形式输出,从而完整保留数字字面量(包括尾部零)。
- 适用于单个字段,简单轻量
- 序列化结果为 JSON 字符串,如
"12.30",而非数字12.30 - 示例:
@JsonFormat(shape = JsonFormat.Shape.STRING) private BigDecimal amount;
全局配置 ObjectMapper 使用 BigDecimalSerializer
若项目中大量使用 BigDecimal 且需统一保留精度,可注册自定义序列化器。Jackson 提供了 BigDecimalSerializer 的子类,或直接使用 ToStringSerializer,但更推荐继承 StdScalarSerializer 并重写 serialize 方法,调用 bigDecimal.toPlainString()(而非 toString())。
-
toPlainString()不用科学计数法,且保留所有尾部零(如new BigDecimal("12.30").toPlainString()→"12.30") - 配置方式:
ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new SimpleModule().addSerializer( BigDecimal.class, new JsonSerializer<BigDecimal>() { @Override public void serialize(BigDecimal value, JsonGenerator gen, SerializerProvider serializers) throws IOException { gen.writeString(value == null ? null : value.toPlainString()); } } ));
避免使用 double 或 float 作为中间类型
开发中常见错误是把 BigDecimal 转成 double 再交给 Jackson(例如误调 doubleValue()),这不仅丢失尾部零,还会引入浮点精度误差(如 0.1 + 0.2 != 0.3)。务必确保字段类型为 BigDecimal,且不经过任何非精确数值转换。
立即学习“Java免费学习笔记(深入)”;
- 检查 DTO 或实体类中是否意外调用了
doubleValue()、floatValue() - 数据库映射(如 MyBatis、JPA)也要确认返回的是
BigDecimal,而非自动转为Double - 前端若需参与计算,建议传字符串并由前端解析,或约定后端统一用
toPlainString()输出
注意反序列化时的精度保持
仅解决序列化还不够。若前端传回 "12.30" 字符串,Jackson 默认会构造 new BigDecimal("12.30"),这是安全的;但如果传的是数字 12.3,Jackson 会解析为 12.3(无尾零)。因此,前后端应约定金额类字段始终以字符串形式传输,或后端接收时显式用 BigDecimal(String) 构造。
- 推荐 API 设计:金额字段在 JSON 中定义为字符串类型(Swagger/OpenAPI 中标注
type: string, format: decimal) - 反序列化时可加
@JsonCreator或自定义反序列化器,确保从字符串构建BigDecimal - 避免依赖 JSON 数字字面量解析,因其本身不携带精度信息


















