Spring Boot 将 JSON 中的 Long 值序列化为 String 避免精度丢失

您所在的位置:网站首页 long类型精度 Spring Boot 将 JSON 中的 Long 值序列化为 String 避免精度丢失

Spring Boot 将 JSON 中的 Long 值序列化为 String 避免精度丢失

2024-07-03 14:24| 来源: 网络整理| 查看: 265

什么是精度丢失?

Java 中长整形 Long (64位)的取值范围是:-9223372036854775808 - 9223372036854775807。

在这种情况下,由于 JavaScript 的 Number 类型是 64 位浮点数,它无法精确表示超过 53 位的整数。因此,当将 Java Long 类型的值传递给 JavaScript 时,可能会发生精度丢失。

你可以在浏览器控制台运行如下代码,更直观地感受 “精度丢失” 的问题。

let val = 9223372036854775807; console.log(val); //9223372036854776000 输出的值丢失了精度

如果我们在业务中使用 Long 作为数据类型,那么就必须要考虑浏览器客户端中 Js 存在精度丢失的问题。解决这个问题最简单的办法就是 把 Java 的 Long 值,序列化为字符串传递。

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"}

全局设置成功。系统中所有的 Long 类型,都会被序列化为 String。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3