Spring Boot 将 JSON 中的 Long 值序列化为 String 避免精度丢失
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
整形ID转字符串
@JsonSerialize(using = ToStringSerializer.class)
什么是精度丢失?
Java 中长整形 Long
(64位)的取值范围是:-9223372036854775808
- 9223372036854775807
。
在这种情况下,由于 JavaScript 的 Number
类型是 64 位浮点数,它无法精确表示超过 53 位的整数。因此,当将 Java Long
类型的值传递给 JavaScript 时,可能会发生精度丢失。
你可以在浏览器控制台运行如下代码,更直观地感受 “精度丢失” 的问题。
let val = 9223372036854775807; console.log(val); //9223372036854776000 输出的值丢失了精度
Jackson 的注解支持
Spring Boot 默认使用 Jackson 作为 JSON 的序列化、反序列化框架。Jackson 提供了 @JsonSerialize
注解,该注解的 using
属性可以指定一个 JsonSerializer
的实现类,用于自定义字段的序列化方式。
Jackson 已经预定义了一个实现 ToStringSerializer
,用于把指定的字段序列化为字符串。
定义一个简单的 User
对象:
package cn.springdoc.demo.model; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; public class User { // 把 Long 类型的 id 序列化为 字符串 @JsonSerialize(using = ToStringSerializer.class) private Long id; private String name; // 忽略 get / set 方法 }
定义一个 Controller 进行测试:
package cn.springdoc.demo.web.controller; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import cn.springdoc.demo.model.User; @RestController @RequestMapping("/demo") public class DemoController { @PostMapping public User demo (@RequestBody User user) { return user; } }
如上,定义一个简单的端点,接收客户端 POST 的 JSON 字符串,封装为 User
对象后原样返回。
启动应用,使用 curl 进行测试:
$ curl -X POST -H "Content-Type: application/json" -d '{"id": 9223372036854775807, "name": "springdoc.cn"}' http://localhost:8080/demo {"id":"9223372036854775807","name":"springdoc.cn"}
如你所见,请求体中的 id
字段是一个数值类型,其值为 9223372036854775807
,也就是 Long 的最大值。Jackson 准确地把请求体封装为了 User
对象。
并且在响应 JSON 中,id
字段类型是 String
,说明注解生效!
id
字段如果以字符串形式传递,Jackson 也能自动解析为 Long
类型:
$ curl -X POST -H "Content-Type: application/json" -d '{"id": "9223372036854775807", "name": "springdoc.cn"}' http://localhost:8080/demo {"id":"9223372036854775807","name":"springdoc.cn"}
全局设置
@JsonSerialize
注解的弊端在于,需要对所有 Long
类型的字段进行一一设置,这太麻烦。
通过 Spring Boot 的 Jackson2ObjectMapperBuilderCustomizer
配置类,可以对 Jackson 进自定义,从而指定所有 Long
类型的序列化方式为 String
。
package cn.springdoc.demo.configuration; import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; @Configuration public class JacksonConfiguration { @Bean public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() { return builder -> { // 把 Long 类型序列化为 String builder.serializerByType(Long.class, ToStringSerializer.instance); }; } }
删除 User
对象中的注解:
package cn.springdoc.demo.model; public class User { private Long id; private String name; // 省略 get / set 方法 }
重启应用,再次使用 curl 进行测试:
$ curl -X POST -H "Content-Type: application/json" -d '{"id": "9223372036854775807", "name": "springdoc.cn"}' http://localhost:8080/demo {"id":"9223372036854775807","name":"springdoc.cn"}
注:也可以直接使用该组件替换如上内容
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.math.BigInteger; @Configuration public class JacksonConfig { /** * Jackson全局转化long类型为String,解决jackson序列化时传入前端Long类型缺失精度问题 */ @Bean public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() { return builder -> builder .serializerByType(BigInteger.class, ToStringSerializer.instance) .serializerByType(Long.class, ToStringSerializer.instance); } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律