Java基础 | JSON 处理手册

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):

    yaml 复制代码
    spring:
      jackson:
        # 可选值:
        # - non_null:仅排除 null 字段(推荐,保留空字符串/空集合)
        # - non_empty:排除 null+空字符串+空集合(按需选择)
        default-property-inclusion: non_null
  • 示例实体类:

    java 复制代码
    public 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 ★★★★☆ 备选用法:类级注解(单个类生效)

  • 适用场景:仅某个类需要特殊规则(如部分接口需保留空字段)。

  • 用法示例:

    java 复制代码
    import 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,但个别字段需保留)。

  • 用法示例:

    java 复制代码
    public 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):

    yaml 复制代码
    spring:
      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 已集成依赖,仅需配置注册)
  • 示例实体类:

    java 复制代码
    import 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"
    }
  • 关键说明:

    1. 时区配置(GMT+8)与 MySQL 时区配置不冲突:MySQL 时区确保存储时间正确,Jackson 时区确保序列化/反序列化时格式正确,二者负责不同阶段;
    2. 无需手动加 JSR310 依赖,Spring Boot web 项目已集成,仅需配置 register-module 注册模块。

2.2 ★★★★☆ 备选用法:字段级注解(自定义单个字段格式)

  • 适用场景:个别字段需特殊格式(如仅日期无时间、多格式兼容)。

  • 用法示例(支持多格式兼容):

    java 复制代码
    import 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-222024-12-22 16:30:00,均可正确转为 LocalDateTime

功能3:隐藏敏感字段(安全防护)

(1)功能目标

序列化时排除密码、身份证号、手机号等敏感信息,避免泄露。

(2)适用场景

用户详情接口、登录响应、个人信息查询等对外接口。

3.1 ★★★★★ 推荐用法:字段级 @JsonIgnore(明确直观)

  • 适用场景:固定敏感字段(如密码),直接排除。

  • 用法示例:

    java 复制代码
    import 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(批量排除)

  • 适用场景:多个敏感字段,批量排除(避免每个字段加注解)。

  • 用法示例:

    java 复制代码
    import 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 ★★★☆☆ 备选用法:自定义序列化器(动态隐藏)

  • 适用场景:敏感字段是否隐藏需动态判断(如管理员可见,普通用户不可见)。

  • 用法示例(简化版):

    java 复制代码
    import 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)。

  • 用法示例:

    java 复制代码
    import 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):

    yaml 复制代码
    spring:
      jackson:
        property-naming-strategy: SNAKE_CASE  # 驼峰自动转下划线(userName→user_name)
  • 示例实体类:

    java 复制代码
    public 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(自定义显示文本)

  • 适用场景:需要自定义枚举显示文本(如中文描述)。

  • 用法示例:

    java 复制代码
    import 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):

    yaml 复制代码
    spring:
      jackson:
        serialization:
          write-enums-using-to-string: true  # 枚举序列化输出名称,而非索引
  • 示例实体类:

    java 复制代码
    public 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(单个值自定义)

  • 适用场景:个别枚举值需要特殊命名(如与前端约定的英文标识)。

  • 用法示例:

    java 复制代码
    import 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(保留必要数据)

  • 适用场景:需要保留父类关联字段,忽略子类关联字段,避免循环。

  • 用法示例:

    java 复制代码
    import 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(简单粗暴)

  • 适用场景:无需返回子类关联字段(如查询订单时无需返回关联用户)。

  • 用法示例:

    java 复制代码
    public 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):

    yaml 复制代码
    spring:
      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 分批处理

  • 适用场景:内存敏感场景,低内存占用序列化超大列表。

  • 用法示例(工具类封装):

    java 复制代码
    import 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
                    }
                }
            }
        }
    }
  • 使用示例(导出用户列表):

    java 复制代码
    import 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 万条),无法一次性查询到内存。

  • 用法示例(伪代码):

    java 复制代码
    public 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:金额分转元(序列化)

    java 复制代码
    import 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 ★★★★★ 推荐用法:工具类方法(直接使用)

  • 用法示例(工具类封装):

    java 复制代码
    import 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);
        }
    }
  • 使用示例:

    java 复制代码
    public 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);
        }
    }
  • 使用示例:

    java 复制代码
    public 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 接收。

  • 用法示例:

    java 复制代码
    import 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):

    yaml 复制代码
    spring:
      jackson:
        deserialization:
          accept-empty-string-as-null-object: true  # 空字符串 "" → null
  • 效果:前端传 {"user_name":"","age":25},后端 userName 字段为 null。

3.1.3 校验处理:强制字段必填

  • 场景:前端必须传某个字段(如用户名),否则反序列化报错。

  • 用法示例:

    java 复制代码
    import 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 泛型类)

  • 场景:所有接口返回统一结构(状态码+提示信息+数据),前端无需适配不同格式。

  • 用法示例(统一响应类):

    java 复制代码
    import 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):

    yaml 复制代码
    spring:
      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
  • 关键说明:
    1. 无需手动添加 JSR310 依赖,Spring Boot web 项目已集成,仅需配置 register-module
    2. 所有配置均为项目通用最优解,无冗余,直接复制可用。

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-databindjackson-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);
    }
}
相关推荐
_OP_CHEN2 小时前
【Python基础】(五)Python 库使用全攻略:从标准库到第三方库,让开发效率翻倍
开发语言·python·pip·项目实战·python标准库·python第三方库
浮尘笔记2 小时前
Go语言条件变量sync.Cond:线程间的协调者
开发语言·后端·golang
北城以北88882 小时前
SpringBoot--Spring Boot原生缓存基于Redis的Cacheable注解使用
java·spring boot·redis·缓存·intellij-idea
武子康2 小时前
Java-208 RabbitMQ Topic 主题交换器详解:routingKey/bindingKey 通配符与 Java 示例
java·分布式·性能优化·消息队列·系统架构·rabbitmq·java-rabbitmq
后端小张3 小时前
【JAVA 进阶】SpringMVC全面解析:从入门到实战的核心知识点梳理
java·开发语言·spring boot·spring·spring cloud·java-ee·springmvc
2301_789015623 小时前
C++:二叉搜索树
c语言·开发语言·数据结构·c++·算法·排序算法
Lucky小小吴4 小时前
ClamAV扫描速度提升6.5倍:服务器杀毒配置优化实战指南
java·服务器·网络·clamav
帅那个帅4 小时前
PHP里面的抽象类和接口类
开发语言·php