JSON 序列化
continew-starter-json-jackson 基于 Jackson 提供开箱即用的 JSON 序列化定制:大数值防精度丢失、Java 8 时间类型与 BaseEnum 枚举的序列化/反序列化,并内置 JsonUtils 与 JsonBuilder 工具。
continew-starter-json-jackson 基于 Jackson 预置了一套符合 RESTful API 最佳实践的 JSON 序列化规则:默认解决 Long 型主键传给前端时的精度丢失问题,完善 LocalDateTime 等 Java 8 时间类型和 BaseEnum 枚举的序列化/反序列化,并提供 JsonUtils、JsonBuilder 两个工具类。
引入依赖
<dependency>
<groupId>top.continew.starter</groupId>
<artifactId>continew-starter-json-jackson</artifactId>
</dependency>模块会传递引入 spring-boot-jackson(Spring Boot 4 默认的 Jackson 3 集成),引入后自动配置 JacksonAutoConfiguration 即刻生效,无需任何额外代码。3.0.0 起 JSON 序列化全面基于 Jackson 3(tools.jackson 包名),实体类上的 @JsonIgnore 等 com.fasterxml.jackson.annotation 包注解可继续沿用(Jackson 3 未改变注解包名)。
配置
模块自带一份默认配置(default-json-jackson.yml),均已按最佳实践设置好,如需覆盖才在你的 application.yml 中声明,否则无需配置:
spring:
mvc:
format:
# 日期格式化(针对 URL 参数中的 java.util.Date)
date-time: yyyy-MM-dd HH:mm:ss
jackson:
# 日期格式化(针对 java.util.Date)
date-format: yyyy-MM-dd HH:mm:ss
# 序列化配置(Bean -> JSON)
serialization:
# 允许序列化无属性的 Bean
fail-on-empty-beans: false
# 反序列化配置(JSON -> Bean)
deserialization:
# 允许 JSON 中存在 Bean 上不存在的属性
fail-on-unknown-properties: false
# 数据类型配置
datatype:
# 日期时间类型配置
datetime:
# 序列化日期时不以 timestamps 输出
write-dates-as-timestamps: false
# 映射配置
mapper:
# 保持属性按声明顺序输出(Jackson 3 默认按字母序)
sort-properties-alphabetically: false
# 大数值序列化模式(模块扩展配置,默认 FLEXIBLE)
big-number-serialize-mode: FLEXIBLE其中 spring.jackson.big-number-serialize-mode 是模块的扩展配置项,对应 top.continew.starter.json.jackson.autoconfigure.JacksonExtensionProperties,可选值:
| 取值 | 行为 | 说明 |
|---|---|---|
FLEXIBLE(默认) | 超出 JS 安全整数范围的数值转为 String,范围内保持原类型 | 兼顾前端可读性与精度安全 |
TO_STRING | Long/BigInteger/BigDecimal 统一转为 String | 前端始终收到字符串 |
NO_OPERATE | 不做任何处理(不建议) | 超过 JS 范围的数值会损失精度,如 8014753905961037835 会变成 8014753905961038000 |
JS 的 Number.MAX_SAFE_INTEGER 为 9007199254740991,雪花算法等生成的 19 位 ID 已超出该范围,直接以数值返回给前端会丢失末位精度,这就是模块默认开启大数值序列化的原因。
内置序列化能力
自动配置通过 JsonMapperBuilderCustomizer 向全局 JsonMapper 注册了三组 Module:
时间类型
为常用时间类型注册了固定格式的序列化器与反序列化器(Jackson 3 已内置 Java 8 时间类型支持,此处主要覆盖默认格式):
| 类型 | 序列化格式 |
|---|---|
LocalDateTime | yyyy-MM-dd HH:mm:ss |
LocalDate | yyyy-MM-dd |
LocalTime | HH:mm:ss |
Instant | ISO-8601(Jackson 内置) |
Duration | ISO-8601(Jackson 内置) |
BaseEnum 枚举
实现了核心模块 top.continew.starter.core.enums.BaseEnum 接口的枚举,序列化时输出其 getValue() 的值;反序列化时按 getValue() 匹配回枚举常量,匹配不到会抛出 InvalidFormatException。前后端交互因此只需传输枚举值而非枚举名。
大数值
根据 big-number-serialize-mode 的配置,为 Long、long、BigInteger、BigDecimal 注册对应的序列化器(BigNumberSerializer 或 ToStringSerializer),见上文配置表。
FLEXIBLE 模式(默认)下,BigNumberSerializer 以 JS 安全整数闭区间 [-9007199254740991, 9007199254740991] 为判定基准(边界值可被 JS 精确表示):
Long/long/BigInteger:区间内保持数字输出,区间外转为字符串;BigDecimal:绝对值超出该区间,或绝对值不小于 2^51 且无法被 double 精确表示时转为字符串,其余情况保持数字输出。
工具类
JsonUtils
top.continew.starter.json.jackson.util.JsonUtils 是对全局 ObjectMapper 的静态封装,常用方法:
// 对象 -> JSON 字符串
String json = JsonUtils.toJsonStr(user);
// JSON 字符串 -> Bean
User user = JsonUtils.toBean(json, User.class);
// JSON 字符串/JsonNode -> List
List<User> users = JsonUtils.toList(jsonArrayStr, User.class);
// 对象 -> JsonNode
JsonNode node = JsonUtils.parseObj(user);
// 判断字符串是否为 JSON / JSONObject / JSONArray 格式
boolean isJson = JsonUtils.isTypeJson(str);
boolean isObj = JsonUtils.isTypeJsonObject(str);
boolean isArr = JsonUtils.isTypeJsonArray(str);
// 获取全局 ObjectMapper 自行定制操作
ObjectMapper objectMapper = JsonUtils.getObjectMapper();转换失败时抛出 top.continew.starter.json.jackson.exception.JsonException。
JsonBuilder
top.continew.starter.json.jackson.util.JsonBuilder 以链式方式动态构建 JSON:
String json = JsonBuilder.builder()
.add("code", 200)
.add("msg", "操作成功")
.add("data", user)
.add("list", userList)
.buildString();add 方法支持 String、int、long、boolean、double、JsonNode、Object、List、Map 类型的值,值为 null 时自动跳过该字段;build() 返回 JsonNode,buildString() 返回 JSON 字符串。
注意事项
- 时区:自动配置以 JVM 默认时区(
TimeZone.getDefault())定制ObjectMapper,该设置优先于spring.jackson.time-zone配置。若服务部署在 UTC 等其他时区的容器中,请显式设置 JVM 时区(如-Duser.timezone=Asia/Shanghai),避免时间序列化结果偏移 8 小时。 JsonUtils与JsonBuilder内部通过 HutoolSpringUtil获取 Spring 容器中的ObjectMapper,请在 Spring 环境下使用,不要在容器启动完成前调用。- 从 2.x 升级:
ObjectMapper类型由com.fasterxml.jackson.databind.ObjectMapper变更为tools.jackson.databind.ObjectMapper(不可变对象),直接引用旧类型的代码需同步调整;实体类上的注解(@JsonIgnore等)无需改动。