java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java JSON序列化

Java中JSON序列化与反序列化的原理与实践方法

作者:霸道流氓气质

本文介绍了Java中JSON序列化与反序列化的核心概念,重点剖析了Jackson框架的原理、常用注解及泛型反序列化技巧,文中的示例代码讲解详细,希望对大家有所帮助

一、什么是序列化/反序列化

1.1 基本概念

术语含义方向类比
序列化(Serialize)将 Java 对象转换为字符串/字节流对象 → 字符串把立体模型拍成照片(方便传输)
反序列化(Deserialize)将字符串/字节流还原为 Java 对象字符串 → 对象看照片还原出立体模型

1.2 为什么需要

Java 对象存在于 JVM 内存中,只有当前进程能访问。

要在以下场景使用,必须先转换为通用格式:

1.3 JSON 格式简介

JSON(JavaScript Object Notation)是最通用的数据交换格式:

{
  "orderId": 12345,
  "orderCode": "xx.20260805.000001",
  "amount": 3999.00,
  "paid": true,
  "items": [
    {"skuId": 100, "name": "11", "qty": 1},
    {"skuId": 200, "name": "22", "qty": 2}
  ],
  "address": null
}

JSON 数据类型: 字符串、数字、布尔、null、对象({})、数组([])

二、Java 中的 JSON 框架

框架出品方特点
JacksonFasterXMLSpring 默认集成,功能最全
Fastjson阿里巴巴性能极高,API 简洁
GsonGoogle轻量级,容错性好

三、Jackson 核心原理

3.1 序列化过程(对象 → JSON)

Java 对象
  │
  ├─ ObjectMapper 通过反射获取类的所有字段
  │   ├─ 遍历 getter 方法(默认行为)
  │   ├─ 或遍历字段(配置后)
  │   └─ 检查注解(@JsonIgnore、@JsonProperty 等)
  │
  ├─ 对每个字段:
  │   ├─ 获取字段名 → 作为 JSON key
  │   ├─ 获取字段值 → 转换为 JSON value
  │   │   ├─ String → "字符串"
  │   │   ├─ Integer/Long → 数字
  │   │   ├─ Boolean → true/false
  │   │   ├─ null → null
  │   │   ├─ List/Array → [...]
  │   │   ├─ 嵌套对象 → {...}(递归序列化)
  │   │   └─ Date → 时间戳或格式化字符串
  │   └─ 拼接为 "key": value
  │
  └─ 输出完整 JSON 字符串

3.2 反序列化过程(JSON → 对象)

JSON 字符串
  │
  ├─ ObjectMapper 解析 JSON 为 Token 流
  │   (类似词法分析:{ → START_OBJECT, "name" → FIELD_NAME, ...)
  │
  ├─ 根据目标类型 Class<T>:
  │   ├─ 创建目标类实例(无参构造器或 @JsonCreator)
  │   ├─ 遍历 JSON 中的每个 key-value:
  │   │   ├─ 根据 key 找到 Java 类中对应的 setter/字段
  │   │   ├─ 将 JSON value 转换为 Java 类型
  │   │   └─ 调用 setter 设置值
  │   └─ 返回填充完毕的对象
  │
  └─ 未知字段处理:
      ├─ 默认:抛异常 UnrecognizedPropertyException
      └─ 配置 FAIL_ON_UNKNOWN_PROPERTIES=false:忽略

四、核心注解

注解作用示例
@JsonProperty指定 JSON key 名(与 Java 字段名不同时)@JsonProperty("order_code")
@JsonIgnore序列化/反序列化时忽略该字段内部计算字段不返回给前端
@JsonFormat日期格式化@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss")
@JsonInclude控制 null 值是否序列化@JsonInclude(Include.NON_NULL)
@JsonAlias反序列化时接受多个别名@JsonAlias({"name","userName"})

五、泛型反序列化(TypeReference)

5.1 问题:泛型擦除

// ❌ 编译期泛型信息会被擦除
List<OrderDto> list = objectMapper.readValue(json, List.class);
// 实际得到的是 List<LinkedHashMap>,不是 List<OrderDto>!
// 因为运行时 JVM 不知道 List 里应该是什么类型

5.2 解决方案:TypeReference

// ✅ TypeReference 保留了完整的泛型信息
List<OrderDto> list = objectMapper.readValue(json, 
    new TypeReference<List<OrderDto>>() {});
// 正确反序列化为 List<OrderDto>

// 嵌套泛型
RestControllerResult<List<OrderDto>> result = objectMapper.readValue(json,
    new TypeReference<RestControllerResult<List<OrderDto>>>() {});

原理: TypeReference 通过匿名内部类在编译时保留了泛型参数信息(写入 class 文件的 Signature 属性中),运行时可以通过反射读取。

六、通用示例代码

6.1 Spring MVC 自动序列化(最常用)

/**
 * Spring MVC 自动处理 JSON 序列化/反序列化.
 * 无需手动调用 ObjectMapper.
 */
@RestController
public class OrderController {

    /**
     * 接收 JSON 请求体 → 自动反序列化为 Java 对象.
     * 返回 Java 对象 → 自动序列化为 JSON 响应.
     */
    @PostMapping("/api/order/create")
    public RestControllerResult<OrderDto> createOrder(@RequestBody CreateOrderDto dto) {
        // Spring 自动完成:
        // 1. 请求体 JSON → CreateOrderDto(反序列化)
        // 2. 返回值 RestControllerResult → JSON 响应体(序列化)
        OrderDto result = orderService.create(dto);
        return new RestControllerResult<>(result);
    }
}

// 前端发送:
// POST /api/order/create
// Content-Type: application/json
// {"orderCode":"SO.001","amount":3999.00,"items":[{"skuId":1,"qty":2}]}

// 后端返回:
// {"success":true,"code":0,"data":{"orderId":123,"orderCode":"SO.001",...}}

6.2 手动序列化/反序列化

/**
 * 手动使用 ObjectMapper 的场景:
 * - 日志记录对象内容
 * - 调用第三方接口组装请求体
 * - Redis 缓存存取
 * - MQ 消息体
 */
public class JsonDemo {

    // 全局单例 ObjectMapper(线程安全,不要每次 new)
    private static final ObjectMapper objectMapper = new ObjectMapper()
        .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)  // 忽略未知字段
        .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true);     // 日期转时间戳

    /**
     * 对象 → JSON 字符串.
     */
    public static String toJson(Object obj) {
        try {
            return objectMapper.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            log.error("JSON序列化失败", e);
            return "";
        }
    }

    /**
     * JSON 字符串 → 对象.
     */
    public static <T> T fromJson(String json, Class<T> clazz) {
        try {
            return objectMapper.readValue(json, clazz);
        } catch (Exception e) {
            log.error("JSON反序列化失败", e);
            return null;
        }
    }

    /**
     * JSON 字符串 → 泛型对象(如 List<T>、Map<K,V>).
     */
    public static <T> T fromJson(String json, TypeReference<T> typeRef) {
        try {
            return objectMapper.readValue(json, typeRef);
        } catch (Exception e) {
            log.error("JSON反序列化失败", e);
            return null;
        }
    }
}

6.3 Fastjson 用法(阿里巴巴)

/**
 * Fastjson 
 * 
 */
public class FastjsonDemo {

    // 序列化
    String json = JSON.toJSONString(orderDto);

    // 反序列化 - 简单类型
    OrderDto dto = JSON.parseObject(json, OrderDto.class);

    // 反序列化 - 泛型类型
    DuResultDto<List<QueryResultDto>> result = JSONObject.parseObject(
        responseStr,
        new TypeReference<DuResultDto<List<QueryResultDto>>>() {});

    // 获取 JSON 中某个字段
    JSONObject jsonObject = JSONObject.parseObject(responseStr);
    String success = jsonObject.getString("isSuccess");
    Integer code = jsonObject.getInteger("code");
}

6.4 常用注解示例

@Data
public class DeliveryOrderDto {

    private Integer id;

    // JSON key 与 Java 字段名不同
    @JsonProperty("delivery_code")
    private String deliveryCode;

    // 序列化时忽略(内部使用,不返回前端)
    @JsonIgnore
    private Date achieveUserOrderCutDate;

    // 日期格式化
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date createTime;

    // null 值不序列化到 JSON 中
    @JsonInclude(JsonInclude.Include.NON_NULL)
    private String remark;

    // 反序列化时接受多个字段名(兼容不同上游系统)
    @JsonAlias({"shuttleBus", "shuttle_bus", "busSchedule"})
    private List<String> shuttleBus;
}

// 序列化结果:
// {"id":1,"delivery_code":"SO.001","createTime":"2026-08-05 10:00:00","shuttleBus":["1","3","5"]}
// 注意:achieveUserOrderCutDate 被忽略,remark 为 null 时不输出

6.5 驼峰与下划线自动转换

/**
 * 对接第三方系统时,对方用下划线命名,我方用驼峰.
 */

// 方式1:全局配置 ObjectMapper
ObjectMapper snakeMapper = new ObjectMapper()
    .setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);

// Java 对象 {"orderCode":"SO.001"} → JSON {"order_code":"SO.001"}
String json = snakeMapper.writeValueAsString(dto);

// 方式2:类级别注解
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public class ThirdPartyResponseDto {
    private String orderCode;       // JSON 中对应 "order_code"
    private Integer deliveryWay;    // JSON 中对应 "delivery_way"
}

七、关键设计总结

设计要点实现方式收益
ObjectMapper 单例静态 final 全局复用避免重复创建的性能开销
忽略未知字段FAIL_ON_UNKNOWN_PROPERTIES=false上游加字段不影响下游反序列化
泛型反序列化TypeReference正确还原嵌套泛型类型
日期处理@JsonFormat统一日期格式,避免前端解析问题
空值控制@JsonInclude(NON_NULL)减少传输数据量
异常容错try-catch 返回 null/空字符串序列化失败不阻断业务
命名策略SnakeCase / CamelCase适配不同系统的命名风格

到此这篇关于Java中JSON序列化与反序列化的原理与实践方法的文章就介绍到这了,更多相关Java JSON序列化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文