Java基础 | JSON 处理手册
- 一、前置准备(基础依赖+核心工具,必看)
-
- [1.1 依赖引入(区分项目类型)](#1.1 依赖引入(区分项目类型))
-
- [1.1.1 Spring Boot 项目](#1.1.1 Spring Boot 项目)
- [1.1.2 普通 Java 项目(Maven)](#1.1.2 普通 Java 项目(Maven))
- 二、核心功能点(按使用频率排序,用法+优先级+效果)
-
- [功能1:排除 null/空字段(精简 JSON 数据)](#功能1:排除 null/空字段(精简 JSON 数据))
-
- [1.1 ★★★★★ 推荐用法:项目级全局配置(所有类生效)](#1.1 ★★★★★ 推荐用法:项目级全局配置(所有类生效))
- [1.2 ★★★★☆ 备选用法:类级注解(单个类生效)](#1.2 ★★★★☆ 备选用法:类级注解(单个类生效))
- [1.3 ★★★☆☆ 备选用法:字段级注解(单个字段生效)](#1.3 ★★★☆☆ 备选用法:字段级注解(单个字段生效))
- 功能2:日期/时间格式统一(避免时差+格式混乱)
-
- [2.1 ★★★★★ 推荐用法:项目级全局配置(所有日期生效)](#2.1 ★★★★★ 推荐用法:项目级全局配置(所有日期生效))
- [2.2 ★★★★☆ 备选用法:字段级注解(自定义单个字段格式)](#2.2 ★★★★☆ 备选用法:字段级注解(自定义单个字段格式))
- 功能3:隐藏敏感字段(安全防护)
-
- [3.1 ★★★★★ 推荐用法:字段级 @JsonIgnore(明确直观)](#3.1 ★★★★★ 推荐用法:字段级 @JsonIgnore(明确直观))
- [3.2 ★★★★☆ 备选用法:类级 @JsonIgnoreProperties(批量排除)](#3.2 ★★★★☆ 备选用法:类级 @JsonIgnoreProperties(批量排除))
- [3.3 ★★★☆☆ 备选用法:自定义序列化器(动态隐藏)](#3.3 ★★★☆☆ 备选用法:自定义序列化器(动态隐藏))
- [功能4:自定义 JSON 字段名(适配前后端/数据库)](#功能4:自定义 JSON 字段名(适配前后端/数据库))
-
- [4.1 ★★★★★ 推荐用法:字段级 @JsonProperty(双向生效)](#4.1 ★★★★★ 推荐用法:字段级 @JsonProperty(双向生效))
- [4.2 ★★★☆☆ 备选用法:全局驼峰转下划线(仅序列化生效)](#4.2 ★★★☆☆ 备选用法:全局驼峰转下划线(仅序列化生效))
- 功能5:枚举格式统一(前端可读)
-
- [5.1 为什么默认会输出 0/1?](#5.1 为什么默认会输出 0/1?)
- [5.2 ★★★★★ 推荐用法:枚举类 @JsonValue(自定义显示文本)](#5.2 ★★★★★ 推荐用法:枚举类 @JsonValue(自定义显示文本))
- [5.3 ★★★★☆ 备选用法:项目级全局配置(枚举转名称字符串)](#5.3 ★★★★☆ 备选用法:项目级全局配置(枚举转名称字符串))
- [5.4 ★★★☆☆ 备选用法:枚举值 @JsonProperty(单个值自定义)](#5.4 ★★★☆☆ 备选用法:枚举值 @JsonProperty(单个值自定义))
- 功能6:解决嵌套对象循环引用(避坑核心)
-
- [6.1 ★★★★★ 推荐用法:@JsonManagedReference + @JsonBackReference(保留必要数据)](#6.1 ★★★★★ 推荐用法:@JsonManagedReference + @JsonBackReference(保留必要数据))
- [6.2 ★★★★☆ 备选用法:字段级 @JsonIgnore(简单粗暴)](#6.2 ★★★★☆ 备选用法:字段级 @JsonIgnore(简单粗暴))
- [6.3 ★★★☆☆ 备选用法:全局配置(不报错,不推荐)](#6.3 ★★★☆☆ 备选用法:全局配置(不报错,不推荐))
- [功能7:处理超大 JSON(避免 OOM)](#功能7:处理超大 JSON(避免 OOM))
-
- [7.1 ★★★★★ 推荐用法:流式 API 分批处理](#7.1 ★★★★★ 推荐用法:流式 API 分批处理)
- [7.2 ★★★☆☆ 备选用法:分页查询+流式输出(超大数据量)](#7.2 ★★★☆☆ 备选用法:分页查询+流式输出(超大数据量))
- 功能8:自定义序列化器/反序列化器(特殊格式处理)
-
- [8.1 ★★★★☆ 推荐用法:字段级 @JsonSerialize/@JsonDeserialize(局部生效)](#8.1 ★★★★☆ 推荐用法:字段级 @JsonSerialize/@JsonDeserialize(局部生效))
- [8.2 ★★★☆☆ 备选用法:全局注册序列化器(所有同类型字段生效)](#8.2 ★★★☆☆ 备选用法:全局注册序列化器(所有同类型字段生效))
- [功能9:JSON 与 Map/List 互转(无实体类场景)](#功能9:JSON 与 Map/List 互转(无实体类场景))
-
- [9.1 ★★★★★ 推荐用法:工具类方法(直接使用)](#9.1 ★★★★★ 推荐用法:工具类方法(直接使用))
- [功能10:JSON 字符串格式化(调试专用)](#功能10:JSON 字符串格式化(调试专用))
-
- [10.1 ★★★★★ 推荐用法:工具类方法](#10.1 ★★★★★ 推荐用法:工具类方法)
- 三、专项场景聚合(按操作类型/业务场景归类)
-
- [3.1 反序列化专项(JSON→Java,前端传参适配)](#3.1 反序列化专项(JSON→Java,前端传参适配))
-
- [3.1.1 基础用法:接收前端 DTO(含忽略多传字段)](#3.1.1 基础用法:接收前端 DTO(含忽略多传字段))
- [3.1.2 兼容处理:空字符串转 null](#3.1.2 兼容处理:空字符串转 null)
- [3.1.3 校验处理:强制字段必填](#3.1.3 校验处理:强制字段必填)
- [3.2 序列化专项(Java→JSON,后端返回适配)](#3.2 序列化专项(Java→JSON,后端返回适配))
-
- [3.2.1 基础用法:普通对象/列表序列化](#3.2.1 基础用法:普通对象/列表序列化)
- [3.2.2 规范用法:统一响应格式(Result 泛型类)](#3.2.2 规范用法:统一响应格式(Result 泛型类))
- [3.2.3 优化用法:空集合序列化为 [](而非 null)](#3.2.3 优化用法:空集合序列化为 [](而非 null))
- 四、全局/项目级配置(统一规则,减少重复注解)
-
- [4.1 Spring Boot 项目全局配置(完整清单)](#4.1 Spring Boot 项目全局配置(完整清单))
- [4.2 普通 Java 项目全局配置(非 Spring 环境)](#4.2 普通 Java 项目全局配置(非 Spring 环境))
- [4.3 优先级说明](#4.3 优先级说明)
- 五、常用工具类封装(直接复制可用)
一、前置准备(基础依赖+核心工具,必看)
1.1 依赖引入(区分项目类型)
1.1.1 Spring Boot 项目
spring-boot-starter-web 依赖已默认集成 Jackson 核心包 + jackson-datatype-jsr310 包 (支持 Java 8 时间类),无需额外引入任何依赖,直接使用即可。
(注:无需手动添加 jackson-datatype-jsr310 依赖,web starter 已自动集成,避免版本冲突)
1.1.2 普通 Java 项目(Maven)
需手动引入以下依赖,推荐稳定版本 2.15.x(兼容 Java 8+):
xml
<!-- 核心依赖:Jackson 核心功能(注解+序列化/反序列化) -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>
<!-- 补充依赖:支持 Java 8 时间类(LocalDateTime/LocalDate),必须手动引入 -->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.15.2</version>
</dependency>
二、核心功能点(按使用频率排序,用法+优先级+效果)
功能1:排除 null/空字段(精简 JSON 数据)
(1)功能目标
序列化时过滤值为
null、空字符串("")、空集合([])的无用字段,减少传输体积。
(2)适用场景
接口返回数据、缓存序列化、数据导出。
1.1 ★★★★★ 推荐用法:项目级全局配置(所有类生效)
-
适用场景:整个项目统一规则,无需逐个类/字段注解。
-
配置方式(Spring Boot 项目,application.yml):
yamlspring: jackson: # 可选值: # - non_null:仅排除 null 字段(推荐,保留空字符串/空集合) # - non_empty:排除 null+空字符串+空集合(按需选择) default-property-inclusion: non_null -
示例实体类:
javapublic class User { private String userName = "张三"; // 非 null 字段 private Integer age = null; // null 字段 private List<String> tags = new ArrayList<>(); // 空集合 } -
序列化效果(JSON 结果):
json{"userName":"张三","tags":[]} // age(null)被排除,tags(空集合)保留(non_null 配置)若配置为
non_empty,效果为:json{"userName":"张三"} // age(null)、tags(空集合)均被排除
1.2 ★★★★☆ 备选用法:类级注解(单个类生效)
-
适用场景:仅某个类需要特殊规则(如部分接口需保留空字段)。
-
用法示例:
javaimport com.fasterxml.jackson.annotation.JsonInclude; // 该类所有 null 字段不序列化(覆盖全局配置) @JsonInclude(JsonInclude.Include.NON_NULL) // 若需排除空字符串/空集合,改为:@JsonInclude(JsonInclude.Include.NON_EMPTY) public class User { private String userName = "张三"; private Integer age = null; private List<String> tags = new ArrayList<>(); } -
序列化效果:与全局配置对应规则一致(NON_NULL 排除 null,NON_EMPTY 排除所有空值)。
1.3 ★★★☆☆ 备选用法:字段级注解(单个字段生效)
-
适用场景:仅某个字段需要特殊规则(如大部分字段排除 null,但个别字段需保留)。
-
用法示例:
javapublic class User { private String userName = "张三"; // 仅该字段排除 null(其他字段遵循全局/类级规则) @JsonInclude(JsonInclude.Include.NON_NULL) private Integer age = null; private List<String> tags = new ArrayList<>(); } -
序列化效果:仅
age字段的 null 值被排除,其他字段遵循全局规则。
功能2:日期/时间格式统一(避免时差+格式混乱)
(1)功能目标
序列化(Java 日期→JSON 字符串)、反序列化(JSON 字符串→Java 日期)时,统一日期格式,解决 8 小时时差问题。
(2)适用场景
所有涉及日期时间的接口交互、数据存储。
2.1 ★★★★★ 推荐用法:项目级全局配置(所有日期生效)
-
适用场景:整个项目日期格式统一,无需逐个字段注解。
-
配置方式(Spring Boot 项目,application.yml):
yamlspring: jackson: date-format: yyyy-MM-dd HH:mm:ss # 统一日期时间格式 time-zone: GMT+8 # 必配,解决北京时间与 UTC 时差 8 小时问题 register-module: com.fasterxml.jackson.datatype.jsr310.JavaTimeModule # 支持 Java 8 时间类(Spring Boot 已集成依赖,仅需配置注册) -
示例实体类:
javaimport java.time.LocalDateTime; import java.util.Date; public class User { private String userName = "张三"; // Java 8 时间类(推荐) private LocalDateTime createTime = LocalDateTime.of(2024, 12, 22, 16, 30); // 老版本 Date 类(兼容旧项目) private Date birthday = new Date(); } -
序列化效果(JSON 结果):
json{ "userName":"张三", "createTime":"2024-12-22 16:30:00", "birthday":"2024-12-22 16:30:00" } -
关键说明:
- 时区配置(GMT+8)与 MySQL 时区配置不冲突:MySQL 时区确保存储时间正确,Jackson 时区确保序列化/反序列化时格式正确,二者负责不同阶段;
- 无需手动加 JSR310 依赖,Spring Boot web 项目已集成,仅需配置
register-module注册模块。
2.2 ★★★★☆ 备选用法:字段级注解(自定义单个字段格式)
-
适用场景:个别字段需特殊格式(如仅日期无时间、多格式兼容)。
-
用法示例(支持多格式兼容):
javaimport com.fasterxml.jackson.annotation.JsonFormat; import java.time.LocalDateTime; public class User { private String userName = "张三"; // pattern 用逗号分隔多格式,前端传任意一种均可反序列化 @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss,yyyy-MM-dd", timezone = "GMT+8") private LocalDateTime birthday; // 支持 "2024-12-22 16:30:00" 或 "2024-12-22" } -
序列化效果:
json{"userName":"张三","birthday":"2024-12-22"} // 若传入日期+时间,则输出完整格式 -
反序列化效果:前端传
2024-12-22或2024-12-22 16:30:00,均可正确转为LocalDateTime。
功能3:隐藏敏感字段(安全防护)
(1)功能目标
序列化时排除密码、身份证号、手机号等敏感信息,避免泄露。
(2)适用场景
用户详情接口、登录响应、个人信息查询等对外接口。
3.1 ★★★★★ 推荐用法:字段级 @JsonIgnore(明确直观)
-
适用场景:固定敏感字段(如密码),直接排除。
-
用法示例:
javaimport com.fasterxml.jackson.annotation.JsonIgnore; public class User { private String userName = "张三"; @JsonIgnore // 序列化时完全排除该字段,前端无此字段 private String password = "123456"; @JsonIgnore // 排除身份证号 private String idCard = "110101199001011234"; } -
序列化效果(JSON 结果):
json{"userName":"张三"} // 无 password、idCard 字段
3.2 ★★★★☆ 备选用法:类级 @JsonIgnoreProperties(批量排除)
-
适用场景:多个敏感字段,批量排除(避免每个字段加注解)。
-
用法示例:
javaimport com.fasterxml.jackson.annotation.JsonIgnoreProperties; // 批量排除多个敏感字段 @JsonIgnoreProperties({"password", "idCard", "phone"}) public class User { private String userName = "张三"; private String password = "123456"; private String idCard = "110101199001011234"; private String phone = "13800138000"; } -
序列化效果:
json{"userName":"张三"} // password、idCard、phone 均被排除
3.3 ★★★☆☆ 备选用法:自定义序列化器(动态隐藏)
-
适用场景:敏感字段是否隐藏需动态判断(如管理员可见,普通用户不可见)。
-
用法示例(简化版):
javaimport com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import java.io.IOException; // 自定义序列化器:动态隐藏密码(示例:模拟权限判断) public class DynamicPasswordSerializer extends JsonSerializer<String> { @Override public void serialize(String password, JsonGenerator gen, SerializerProvider sp) throws IOException { // 实际场景:从 ThreadLocal 获取当前用户权限 boolean isAdmin = false; if (isAdmin) { gen.writeString(password); // 管理员可见 } else { gen.writeNull(); // 普通用户隐藏(结合排除 null 规则,最终不显示) } } } // 实体类使用 public class User { private String userName = "张三"; @JsonSerialize(using = DynamicPasswordSerializer.class) private String password = "123456"; } -
序列化效果(普通用户):
json{"userName":"张三"} // password 被隐藏
功能4:自定义 JSON 字段名(适配前后端/数据库)
(1)功能目标
实体类遵循 Java 驼峰命名(如
userName),JSON 输出遵循数据库/前端下划线命名(如user_name),解决命名规范不一致问题。
(2)适用场景
前后端字段名约定不一致、实体类与数据库字段名直接映射。
4.1 ★★★★★ 推荐用法:字段级 @JsonProperty(双向生效)
-
适用场景:任意字段名映射,同时支持序列化(Java→JSON)和反序列化(JSON→Java)。
-
用法示例:
javaimport com.fasterxml.jackson.annotation.JsonProperty; public class User { // JSON 输出字段名:user_name(对应数据库字段) @JsonProperty("user_name") private String userName = "张三"; // JSON 输出字段名:u_age(适配前端特殊命名) @JsonProperty("u_age") private Integer age = 25; } -
序列化效果(JSON 结果):
json{"user_name":"张三","u_age":25} -
反序列化效果:前端传
{"user_name":"李四","u_age":30},可自动转为实体类userName="李四"、age=30。
4.2 ★★★☆☆ 备选用法:全局驼峰转下划线(仅序列化生效)
-
适用场景:整个项目实体类均为驼峰命名,JSON 均需转为下划线命名。
-
配置方式(Spring Boot 项目,application.yml):
yamlspring: jackson: property-naming-strategy: SNAKE_CASE # 驼峰自动转下划线(userName→user_name) -
示例实体类:
javapublic class User { private String userName = "张三"; // 驼峰命名 private Integer userAge = 25; // 驼峰命名 } -
序列化效果(JSON 结果):
json{"user_name":"张三","user_age":25} -
注意:此方式仅影响序列化(Java→JSON),反序列化时前端仍需传下划线字段名;且全局生效,灵活性低于
@JsonProperty,谨慎使用。
功能5:枚举格式统一(前端可读)
(1)功能目标
枚举序列化时输出前端可理解的字符串(如"待支付"),而非默认的索引(0/1),避免前端解析歧义。
(2)适用场景
订单状态、支付类型、接口返回码等枚举字段。
5.1 为什么默认会输出 0/1?
Java 枚举本质是特殊类,每个枚举项默认有「索引值」(从 0 开始递增)。若不配置,Jackson 会默认序列化索引(如 PENDING→0、PAID→1),即使代码中未显式定义 0/1,前端也会收到数字,导致歧义。
5.2 ★★★★★ 推荐用法:枚举类 @JsonValue(自定义显示文本)
-
适用场景:需要自定义枚举显示文本(如中文描述)。
-
用法示例:
javaimport com.fasterxml.jackson.annotation.JsonValue; // 订单状态枚举 public enum OrderStatus { PENDING("待支付"), PAID("已支付"), CANCELLED("已取消"); @JsonValue // 序列化时输出该字段值(desc) private final String desc; OrderStatus(String desc) { this.desc = desc; } } // 实体类使用 public class Order { private String orderId = "O001"; private OrderStatus status = OrderStatus.PENDING; } -
序列化效果(JSON 结果):
json{"orderId":"O001","status":"待支付"} // 输出中文描述,前端直接可用
5.3 ★★★★☆ 备选用法:项目级全局配置(枚举转名称字符串)
-
适用场景:无需自定义文本,仅需输出枚举名称(如
PENDING),而非索引。 -
配置方式(Spring Boot 项目,application.yml):
yamlspring: jackson: serialization: write-enums-using-to-string: true # 枚举序列化输出名称,而非索引 -
示例实体类:
javapublic enum OrderStatus { PENDING, PAID, CANCELLED; // 无自定义 desc } public class Order { private String orderId = "O001"; private OrderStatus status = OrderStatus.PENDING; } -
序列化效果(JSON 结果):
json{"orderId":"O001","status":"PENDING"} // 输出枚举名称,而非 0
5.4 ★★★☆☆ 备选用法:枚举值 @JsonProperty(单个值自定义)
-
适用场景:个别枚举值需要特殊命名(如与前端约定的英文标识)。
-
用法示例:
javaimport com.fasterxml.jackson.annotation.JsonProperty; public enum OrderStatus { @JsonProperty("pending") PENDING, @JsonProperty("paid_success") PAID, @JsonProperty("cancel") CANCELLED } public class Order { private String orderId = "O001"; private OrderStatus status = OrderStatus.PAID; } -
序列化效果(JSON 结果):
json{"orderId":"O001","status":"paid_success"} // 输出自定义枚举值
功能6:解决嵌套对象循环引用(避坑核心)
(1)功能目标
避免实体类双向关联(如
User包含List<Order>,Order包含User)导致的序列化无限递归,最终栈溢出(StackOverflowError)。
(2)适用场景
一对多、多对多关联查询(如查询用户时返回关联订单,订单关联用户)。
6.1 ★★★★★ 推荐用法:@JsonManagedReference + @JsonBackReference(保留必要数据)
-
适用场景:需要保留父类关联字段,忽略子类关联字段,避免循环。
-
用法示例:
javaimport com.fasterxml.jackson.annotation.JsonBackReference; import com.fasterxml.jackson.annotation.JsonManagedReference; import java.util.List; // 父类:User(包含订单列表) public class User { private String userName = "张三"; // 标记为父类关联字段,序列化时正常输出 @JsonManagedReference private List<Order> orders; } // 子类:Order(关联用户) public class Order { private String orderId = "O001"; // 标记为子类关联字段,序列化时忽略(避免循环) @JsonBackReference private User user; } // 测试数据:用户关联 1 个订单 User user = new User(); Order order = new Order(); user.setOrders(List.of(order)); order.setUser(user); -
序列化效果(JSON 结果):
json{ "userName":"张三", "orders":[{"orderId":"O001"}] // 无 user 字段,避免循环 }
6.2 ★★★★☆ 备选用法:字段级 @JsonIgnore(简单粗暴)
-
适用场景:无需返回子类关联字段(如查询订单时无需返回关联用户)。
-
用法示例:
javapublic class Order { private String orderId = "O001"; @JsonIgnore // 直接忽略 user 字段,序列化时不输出 private User user; } public class User { private String userName = "张三"; private List<Order> orders; } -
序列化效果:与 6.1 一致,仅输出用户+订单列表,无循环字段。
6.3 ★★★☆☆ 备选用法:全局配置(不报错,不推荐)
-
适用场景:临时调试,不追求数据完整性。
-
配置方式(Spring Boot 项目,application.yml):
yamlspring: jackson: serialization: fail-on-circular-references: false # 循环引用不报错,但可能返回重复数据 -
序列化效果(JSON 结果,简化):
json{ "userName":"张三", "orders":[{"orderId":"O001","user":{"userName":"张三","orders":[{"orderId":"O001",...}]}}] } -
注意:会返回重复嵌套数据,增加传输体积,不推荐生产环境使用。
功能7:处理超大 JSON(避免 OOM)
(1)功能目标
序列化 10 万+ 条数据(如批量导出、大数据同步)时,避免一次性加载所有数据到内存,导致内存溢出(OOM)。
(2)适用场景
大数据量导出、批量数据同步接口。
7.1 ★★★★★ 推荐用法:流式 API 分批处理
-
适用场景:内存敏感场景,低内存占用序列化超大列表。
-
用法示例(工具类封装):
javaimport com.fasterxml.jackson.databind.SequenceWriter; import java.io.OutputStream; import java.util.List; /** * 超大 JSON 处理工具类 */ public class LargeJsonUtils { /** * 分批序列化超大列表 * @param outputStream 输出流(如响应流、文件流) * @param dataList 待序列化数据列表 * @param batchSize 每批处理数量(推荐 1000-5000) */ public static <T> void writeLargeList(OutputStream outputStream, List<T> dataList, int batchSize) throws Exception { // 流式写入:逐条序列化,不一次性加载所有数据 try (SequenceWriter writer = JsonConfig.OBJECT_MAPPER.writer().writeValues(outputStream)) { for (int i = 0; i < dataList.size(); i += batchSize) { int end = Math.min(i + batchSize, dataList.size()); List<T> batch = dataList.subList(i, end); // 分批截取 for (T data : batch) { writer.write(data); // 逐条写入 JSON } } } } } -
使用示例(导出用户列表):
javaimport javax.servlet.http.HttpServletResponse; import java.io.OutputStream; import java.util.List; @RestController public class UserController { @GetMapping("/export/users") public void exportUsers(HttpServletResponse response) throws Exception { response.setContentType("application/json;charset=UTF-8"); OutputStream outputStream = response.getOutputStream(); // 模拟超大列表(实际场景:从数据库分页查询) List<User> userList = userService.getAllUsers(); // 10 万条数据 // 分批序列化(每批 1000 条,内存仅加载 1000 条) LargeJsonUtils.writeLargeList(outputStream, userList, 1000); } } -
效果说明:内存占用稳定在 1000 条数据的大小,无 OOM 风险;数据逐条写入输出流,前端/文件可实时接收。
7.2 ★★★☆☆ 备选用法:分页查询+流式输出(超大数据量)
-
适用场景:数据量超过内存上限(如 100 万条),无法一次性查询到内存。
-
用法示例(伪代码):
javapublic void exportSuperLargeUsers(OutputStream outputStream) throws Exception { int pageNum = 1; int pageSize = 1000; // 每页 1000 条 while (true) { // 数据库分页查询(MyBatis/MP 分页) List<User> userPage = userMapper.selectPage(pageNum, pageSize); if (userPage.isEmpty()) { break; // 无数据则终止 } // 分批写入流 LargeJsonUtils.writeLargeList(outputStream, userPage, pageSize); pageNum++; } } -
效果说明:每次仅查询 1000 条数据,序列化后释放内存,支持无限量数据导出(取决于数据库性能)。
功能8:自定义序列化器/反序列化器(特殊格式处理)
(1)功能目标
处理常规注解无法覆盖的特殊格式需求(如金额分转元、手机号脱敏、自定义编码转换)。
(2)适用场景
金额字段(数据库存分,前端显元)、手机号/身份证号脱敏、特殊编码(如 Base64)转换。
8.1 ★★★★☆ 推荐用法:字段级 @JsonSerialize/@JsonDeserialize(局部生效)
-
适用场景:单个字段的特殊格式处理,不影响其他字段。
-
示例 1:金额分转元(序列化)
javaimport com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import java.io.IOException; // 自定义序列化器:分 → 元(如 1000 分 → 10.00 元) public class CentToYuanSerializer extends JsonSerializer<Integer> { @Override public void serialize(Integer cents, JsonGenerator gen, SerializerProvider sp) throws IOException { if (cents == null) { gen.writeNull(); return; } gen.writeNumber(cents / 100.0); // 分转元,保留小数 } } // 实体类使用 public class Order { private String orderId = "O001"; @JsonSerialize(using = CentToYuanSerializer.class) private Integer amount = 1000; // 数据库存储为分 } -
序列化效果(JSON 结果):
json{"orderId":"O001","amount":10.0} // 1000 分 → 10.0 元 -
示例 2:手机号脱敏(序列化)
java// 自定义序列化器:手机号脱敏(13800138000 → 138****8000) public class PhoneDesensitizeSerializer extends JsonSerializer<String> { @Override public void serialize(String phone, JsonGenerator gen, SerializerProvider sp) throws IOException { if (phone == null || phone.length() != 11) { gen.writeString(phone); return; } // 脱敏规则:第 4-7 位替换为 **** String desensitized = phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2"); gen.writeString(desensitized); } } // 实体类使用 public class User { private String userName = "张三"; @JsonSerialize(using = PhoneDesensitizeSerializer.class) private String phone = "13800138000"; } -
序列化效果(JSON 结果):
json{"userName":"张三","phone":"138****8000"} // 脱敏后输出
8.2 ★★★☆☆ 备选用法:全局注册序列化器(所有同类型字段生效)
-
适用场景:所有同类型字段需要统一特殊处理(如所有金额字段均需分转元)。
-
用法示例:
java// 修改 JsonConfig,全局注册序列化器 public class JsonConfig { public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper() .registerModule(new JavaTimeModule()) // 全局注册:所有 Integer 类型金额字段分转元 .registerSerializer(Integer.class, new CentToYuanSerializer()); } // 实体类无需加注解 public class Order { private String orderId = "O001"; private Integer amount = 1000; // 自动分转元 } -
序列化效果:与 8.1 一致,但所有 Integer 类型字段都会被处理,需谨慎使用(避免非金额字段被误处理)。
功能9:JSON 与 Map/List 互转(无实体类场景)
(1)功能目标
无需定义实体类,直接将 JSON 字符串转为 Map/List(动态字段),或反之,适配临时数据处理场景。
(2)适用场景
前端传动态字段、第三方接口返回非固定格式 JSON、临时数据调试。
9.1 ★★★★★ 推荐用法:工具类方法(直接使用)
-
用法示例(工具类封装):
javaimport java.util.List; import java.util.Map; /** * JSON 与 Map/List 互转工具类 */ public class JsonMapUtils { // 1. JSON 字符串 → Map<String, Object> public static Map<String, Object> jsonToMap(String json) throws Exception { return JsonConfig.OBJECT_MAPPER.readValue(json, Map.class); } // 2. JSON 字符串 → List<Map<String, Object>> public static List<Map<String, Object>> jsonToList(String json) throws Exception { return JsonConfig.OBJECT_MAPPER.readValue(json, List.class); } // 3. Map/List → JSON 字符串 public static String toJson(Object obj) throws Exception { return JsonConfig.OBJECT_MAPPER.writeValueAsString(obj); } } -
使用示例:
javapublic class Test { public static void main(String[] args) throws Exception { // 1. JSON → Map String userJson = "{\"user_name\":\"张三\",\"age\":25}"; Map<String, Object> userMap = JsonMapUtils.jsonToMap(userJson); System.out.println(userMap.get("user_name")); // 输出:张三 // 2. JSON → List<Map> String userListJson = "[{\"user_name\":\"张三\"},{\"user_name\":\"李四\"}]"; List<Map<String, Object>> userList = JsonMapUtils.jsonToList(userListJson); System.out.println(userList.size()); // 输出:2 // 3. Map → JSON Map<String, Object> orderMap = Map.of("order_id", "O001", "amount", 10.0); String orderJson = JsonMapUtils.toJson(orderMap); System.out.println(orderJson); // 输出:{"order_id":"O001","amount":10.0} } } -
效果说明:无需定义实体类,快速处理动态 JSON 数据;缺点是无类型校验,需手动判断字段类型。
功能10:JSON 字符串格式化(调试专用)
(1)功能目标
将紧凑的 JSON 字符串(如日志打印的单行 JSON)格式化为带缩进的美观格式,便于调试查看。
(2)适用场景
日志调试、接口返回数据校验、JSON 文件格式化。
10.1 ★★★★★ 推荐用法:工具类方法
-
用法示例(工具类封装):
java/** * JSON 格式化工具类 */ public class JsonFormatUtils { // 紧凑 JSON → 带 4 个空格缩进的美观 JSON public static String formatJson(String json) throws Exception { Object obj = JsonConfig.OBJECT_MAPPER.readValue(json, Object.class); // 启用默认美化格式 return JsonConfig.OBJECT_MAPPER.writerWithDefaultPrettyPrinter() .writeValueAsString(obj); } } -
使用示例:
javapublic class Test { public static void main(String[] args) throws Exception { // 紧凑 JSON 字符串(如日志输出) String compactJson = "{\"user_name\":\"张三\",\"age\":25,\"createTime\":\"2024-12-22 16:30:00\"}"; // 格式化 String prettyJson = JsonFormatUtils.formatJson(compactJson); System.out.println(prettyJson); } } -
格式化效果(输出结果):
json{ "user_name" : "张三", "age" : 25, "createTime" : "2024-12-22 16:30:00" } -
注意:仅用于调试/日志查看,生产环境建议用紧凑格式(减少传输体积)。
三、专项场景聚合(按操作类型/业务场景归类)
3.1 反序列化专项(JSON→Java,前端传参适配)
(1)核心目标
前端传的 JSON 能正确转为 Java 对象,兼容各种异常情况(多传字段、空字符串、格式不统一)。
3.1.1 基础用法:接收前端 DTO(含忽略多传字段)
-
场景:前端提交表单数据(如用户注册),后端用 DTO 接收。
-
用法示例:
javaimport com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; // 忽略前端多传的未知字段(避免报错) @JsonIgnoreProperties(ignoreUnknown = true) public class UserDTO { @JsonProperty("user_name") // 对应前端下划线字段 private String userName; @JsonProperty("u_age") private Integer age; } // 接口接收(Spring Boot 自动反序列化) @RestController public class UserController { @PostMapping("/register") public String register(@RequestBody UserDTO userDTO) { System.out.println(userDTO.getUserName()); // 前端传 user_name,此处可获取 return "注册成功"; } } -
效果:前端传
{"user_name":"张三","u_age":25,"ext_info":"冗余字段"},后端可正常接收,冗余字段被忽略。
3.1.2 兼容处理:空字符串转 null
-
场景:前端可能传空字符串(
""),后端需转为 null 处理。 -
配置方式(Spring Boot 项目,application.yml):
yamlspring: jackson: deserialization: accept-empty-string-as-null-object: true # 空字符串 "" → null -
效果:前端传
{"user_name":"","age":25},后端userName字段为 null。
3.1.3 校验处理:强制字段必填
-
场景:前端必须传某个字段(如用户名),否则反序列化报错。
-
用法示例:
javaimport com.fasterxml.jackson.annotation.JsonProperty; public class UserDTO { // required = true:前端必须传 user_name 字段,否则报错 @JsonProperty(required = true) private String userName; private Integer age; } -
效果:前端传
{"age":25}(无 user_name),接口返回 400 错误,提示缺少必填字段。
3.2 序列化专项(Java→JSON,后端返回适配)
(1)核心目标
Java 对象转为规范的 JSON,满足前端统一处理需求(格式统一、结构一致)。
3.2.1 基础用法:普通对象/列表序列化
-
场景:接口返回单个对象(如用户详情)或列表(如用户列表)。
-
用法示例:
java@RestController public class UserController { // 返回单个对象 @GetMapping("/user/{id}") public User getUser(@PathVariable String id) { User user = userService.getById(id); return user; // 自动序列化为 JSON } // 返回列表 @GetMapping("/users") public List<User> getUserList() { List<User> userList = userService.list(); return userList; // 自动序列化为 JSON 数组 } } -
效果(列表返回):
json[{"user_name":"张三","age":25},{"user_name":"李四","age":30}]
3.2.2 规范用法:统一响应格式(Result 泛型类)
-
场景:所有接口返回统一结构(状态码+提示信息+数据),前端无需适配不同格式。
-
用法示例(统一响应类):
javaimport com.fasterxml.jackson.annotation.JsonInclude; @JsonInclude(JsonInclude.Include.NON_NULL) public class Result<T> { // 状态码:200=成功,500=失败,400=参数错误 private Integer code; private String msg; private T data; // 业务数据(泛型适配任意类型) // 快速构建方法(无需 new 对象) public static <T> Result<T> success(T data) { return new Result<>(200, "操作成功", data); } public static Result<?> fail(String msg) { return new Result<>(500, msg, null); } // 构造器、getter、setter 省略 } -
接口使用:
java@GetMapping("/user/{id}") public Result<User> getUser(@PathVariable String id) { User user = userService.getById(id); if (user == null) { return Result.fail("用户不存在"); } return Result.success(user); } -
效果(成功/失败):
json// 成功 { "code":200, "msg":"操作成功", "data":{"user_name":"张三","age":25} } // 失败 { "code":500, "msg":"用户不存在" }
3.2.3 优化用法:空集合序列化为 [](而非 null)
-
场景:避免前端处理 null 集合(如
tags: null需判断,tags: []可直接遍历)。 -
配置方式(Spring Boot 项目,application.yml):
yamlspring: jackson: serialization: write-empty-collections-as-null: false # 空集合 → [],而非 null -
效果:实体类
tags = new ArrayList<>(),序列化后为{"tags":[]}。
四、全局/项目级配置(统一规则,减少重复注解)
4.1 Spring Boot 项目全局配置(完整清单)
推荐在项目初始化时配置,覆盖所有常规场景,减少后续注解工作量:
yaml
spring:
jackson:
# 1. 字段控制(排除无用数据)
default-property-inclusion: non_null # 全局排除 null 字段
# 2. 格式统一(日期、时区)
date-format: yyyy-MM-dd HH:mm:ss # 统一日期时间格式
time-zone: GMT+8 # 解决时差问题(与 MySQL 时区不冲突)
register-module: com.fasterxml.jackson.datatype.jsr310.JavaTimeModule # 支持 Java 8 时间类(无需手动加依赖)
# 3. 序列化配置(后端→前端)
serialization:
write-enums-using-to-string: true # 枚举转名称字符串(如 PENDING,避免 0/1)
fail-on-empty-beans: false # 空对象(无字段)不报错
write-empty-collections-as-null: false # 空集合 → []
# 4. 反序列化配置(前端→后端)
deserialization:
fail-on-unknown-properties: false # 忽略前端多传字段
accept-empty-string-as-null-object: true # 空字符串 → null
# 5. 命名策略(默认驼峰,无需修改)
property-naming-strategy: LOWER_CAMEL_CASE
- 关键说明:
- 无需手动添加 JSR310 依赖,Spring Boot web 项目已集成,仅需配置
register-module; - 所有配置均为项目通用最优解,无冗余,直接复制可用。
- 无需手动添加 JSR310 依赖,Spring Boot web 项目已集成,仅需配置
4.2 普通 Java 项目全局配置(非 Spring 环境)
通过代码配置 ObjectMapper,替代 application.yml:
java
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import java.text.SimpleDateFormat;
import java.util.TimeZone;
public class JsonConfig {
public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper()
// 1. 基础配置:支持 Java 8 时间类(需手动加 JSR310 依赖)
.registerModule(new JavaTimeModule())
// 2. 字段控制
.setSerializationInclusion(com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL)
// 3. 日期格式+时区
.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"))
.setTimeZone(TimeZone.getTimeZone("GMT+8"))
// 4. 序列化配置
.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true)
.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
.configure(SerializationFeature.WRITE_EMPTY_COLLECTIONS_AS_NULL, false)
// 5. 反序列化配置
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
}
- 关键说明:普通 Java 项目需手动添加
jackson-databind和jackson-datatype-jsr310依赖,否则报错。
4.3 优先级说明
局部注解(字段/类级,如 @JsonInclude、@JsonProperty)> 全局配置 > Jackson 默认行为。
规则:特殊场景用局部注解覆盖全局配置,常规场景遵循全局配置,确保代码简洁。
五、常用工具类封装(直接复制可用)
整合所有高频操作,封装为统一工具类,无需重复开发:
java
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
/**
* 全能 JSON 工具类(整合所有高频操作)
* 适用说明:
* 1. Spring Boot 项目:无需手动加依赖,直接复制使用
* 2. 普通 Java 项目:需手动添加 jackson-databind + jackson-datatype-jsr310 依赖
*/
public class JsonUtils {
// 全局单例 ObjectMapper(配置完整)
private static final com.fasterxml.jackson.databind.ObjectMapper OBJECT_MAPPER = new com.fasterxml.jackson.databind.ObjectMapper()
.registerModule(new JavaTimeModule())
// 注册自定义序列化器(示例:金额分转元)
.registerModule(new SimpleModule().addSerializer(Integer.class, new CentToYuanSerializer()))
// 基础配置
.setSerializationInclusion(com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL)
.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"))
.setTimeZone(TimeZone.getTimeZone("GMT+8"))
.configure(com.fasterxml.jackson.databind.SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true)
.configure(com.fasterxml.jackson.databind.SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
.configure(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// 1. Java 对象 → JSON 字符串
public static String toJson(Object obj) throws Exception {
return OBJECT_MAPPER.writeValueAsString(obj);
}
// 2. JSON 字符串 → Java 对象
public static <T> T fromJson(String json, Class<T> clazz) throws Exception {
return OBJECT_MAPPER.readValue(json, clazz);
}
// 3. JSON 字符串 → Map<String, Object>
public static Map<String, Object> toMap(String json) throws Exception {
return OBJECT_MAPPER.readValue(json, Map.class);
}
// 4. JSON 字符串 → List<Map<String, Object>>
public static List<Map<String, Object>> toList(String json) throws Exception {
return OBJECT_MAPPER.readValue(json, List.class);
}
// 5. JSON 格式化(紧凑→美观)
public static String formatJson(String json) throws Exception {
Object obj = OBJECT_MAPPER.readValue(json, Object.class);
return OBJECT_MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
}
// 6. 超大列表分批序列化(避免 OOM)
public static <T> void writeLargeList(OutputStream outputStream, List<T> dataList, int batchSize) throws Exception {
com.fasterxml.jackson.databind.SequenceWriter writer = OBJECT_MAPPER.writer().writeValues(outputStream);
for (int i = 0; i < dataList.size(); i += batchSize) {
int end = Math.min(i + batchSize, dataList.size());
List<T> batch = dataList.subList(i, end);
for (T data : batch) {
writer.write(data);
}
}
writer.close();
}
// 自定义序列化器:金额分转元(示例)
public static class CentToYuanSerializer extends JsonSerializer<Integer> {
@Override
public void serialize(Integer cents, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeNumber(cents == null ? 0 : cents / 100.0);
}
}
// 私有化构造器,禁止实例化
private JsonUtils() {}
}
工具类使用示例
java
public class Test {
public static void main(String[] args) throws Exception {
// 1. 对象转 JSON
User user = new User();
user.setUserName("张三");
user.setAge(25);
String json = JsonUtils.toJson(user);
System.out.println(json); // {"user_name":"张三","u_age":25}
// 2. JSON 转对象
User user2 = JsonUtils.fromJson(json, User.class);
System.out.println(user2.getUserName()); // 张三
// 3. JSON 格式化
String prettyJson = JsonUtils.formatJson(json);
System.out.println(prettyJson);
// 4. 超大列表序列化(假设 userList 是 10 万条数据)
// OutputStream os = new FileOutputStream("users.json");
// JsonUtils.writeLargeList(os, userList, 1000);
}
}