1. 什么是 JSONUtil?
- 含义:它是 Hutool 工具库中专门用于处理 JSON 数据的静态工具类。
- 作用 :帮你把 Java 对象 变成 JSON 字符串 (序列化),或者把 JSON 字符串 变成 Java 对象(反序列化)。
- 特点:
- 零配置:不需要像 Jackson 或 Gson 那样配置 ObjectMapper
- 静态方法 :直接通过
类名.方法名()调用,无需new对象。 - 容错性强:对字段缺失、类型不匹配等情况处理比较宽容。
2.前置准备
maven:
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.26</version> <!-- 建议使用最新稳定版 -->
</dependency>
3.使用
1.java对象->java字符串(序列化)
假设这有一个实体类user:
public class User {
private String name;
private Integer age;
// 省略 getter, setter, 构造函数
}
User user = new User("张三", 18);
| 需求 | 代码示例 | 结果示例 |
|---|---|---|
| 转为普通 JSON | String json=JSONUtil.toJsonStr(user); |
{"name":"张三","age":18} |
| 转为格式化 JSON (带换行缩进,方便看) | String prettyJson = JSONUtil.toJsonPrettyStr(user); |
{\n "name": "张三",\n "age": 18\n} |
| 转为 JSONObject 对象 (方便后续动态操作) | JSONObject jsonObj = JSONUtil.parseObj(user); |
内存中的 JSON 对象 |
2.JSON 字符串 -> Java 对象 (反序列化)
假设我们有一个 JSON 字符串:
String jsonStr = "{\"name\":\"李四\",\"age\":20}";
| 需求 | 代码示例 | 说明 |
|---|---|---|
| 转为指定实体类 | User user = JSONUtil.toBean(jsonStr, User.class); |
最常用!直接变回 User 对象 |
| 转为 Map 集合 | Map<String, Object> map = JSONUtil.toBean(jsonStr, Map.class); |
当不知道数据结构,只想拿数据时用 |
| 转为 JSONObject | JSONObject jsonObj = JSONUtil.parseObj(jsonStr); |
转为 Hutool 特有的 JSON 对象,可用 getStr() 等方法取值 |
| 转为 List 列表 | List<User> list = JSONUtil.toList(jsonArrayStr, User.class); |
注意:第一个参数必须是 JSON 数组字符串 [...] |
3.动态构建 JSON (不创建实体类)
场景:临时拼凑一个 JSON 发给前端,不想专门写个 Java 类。
// 1. 创建一个空的 JSONObject
JSONObject jsonObj = JSONUtil.createObj();
// 2. 链式调用放入数据
jsonObj.put("id", 1001)
.put("username", "admin")
.put("isActive", true)
.put("roles", JSONUtil.createArray().put("ROLE_USER").put("ROLE_ADMIN"));
// 3. 转为字符串
String result = jsonObj.toString();
// 输出: {"id":1001,"username":"admin","isActive":true,"roles":["ROLE_USER","ROLE_ADMIN"]}
4.为什么要序列化
简单来说,Java 对象是"给机器(JVM)看的",而 JSON 字符串是"给人和不同系统看的"。
具体说:
1. 跨语言通信
- 问题 :Java 对象是 Java 虚拟机(JVM)特有的内存结构。如果你用 Java 写了一个后端服务,而前端是用 JavaScript 写的,或者另一个微服务是用 Python/Go 写的,它们根本看不懂 Java 的内存对象(比如
User@1a2b3c这种引用地址和内部二进制结构)。 - 解决 :JSON 是一种纯文本格式 ,它是语言无关 的。
- Java 把对象转成 JSON 字符串。
- 字符串通过网络发送给前端。
- 前端(JS)、Python、Go 都能轻松读懂这个字符串,并转换成它们自己的对象。
- 比喻:Java 对象像是"中文书",Python 像是"只懂英文的人"。JSON 就是"世界语",大家都把它翻译成世界语交流,对方就能懂了。
2. 网络传输
- 问题 :在网络上传输数据时(比如 HTTP 请求),数据必须以字节流的形式发送。复杂的 Java 对象包含引用关系、类元信息等,直接传二进制流不仅体积大,而且接收方如果没有完全相同的类定义,就无法还原。
- 解决 :JSON 是轻量级的文本格式。
- 它将复杂的对象结构"拍扁"成一行行文字。
- 体积小,解析速度快。
- 天然适合 HTTP 协议。
3. 解耦与安全性
- 问题 :Java 对象可能包含很多内部细节,比如:
- 私有字段(
private)。 - 临时变量(
transient)。 - 数据库密码、内部 ID 等敏感信息。
- 复杂的继承关系或代理对象(如 Spring AOP 生成的对象)。
- 如果直接把 Java 对象二进制序列化发给前端,不仅不安全,还可能导致前端解析崩溃。
- 私有字段(
- 解决 :通过转为 JSON,我们可以精确控制 哪些字段暴露出去。
- 例如:使用
@JsonIgnore注解忽略密码字段。 - 只返回前端需要的
name和age,隐藏内部的dbConnection或salary。 - 这实现了数据传输层 与业务逻辑层的解耦。
- 例如:使用
4. 数据存储与缓存
- 问题:内存中的 Java 对象是"易失"的,程序一停,对象就没了。而且像 Redis 这样的通用缓存数据库,通常存储的是字符串或简单的键值对。
- 解决 :
- 存数据库:很多 NoSQL 数据库直接存储 JSON 文档。
- 存缓存:把用户会话(Session)或热点数据转成 JSON 字符串存入 Redis,下次取出来再反序列化回对象,非常高效。
- 日志记录 :打印日志时,直接打印
user.toString()可能是一堆乱码或内存地址,而JSONUtil.toJsonStr(user)能打印出清晰的结构化数据,方便排查问题。
5.为什么反序列化
如果说"序列化"是为了把数据送出去 ,那么"反序列化"就是为了把数据接回来并重新利用。
这是一个对称的过程。没有反序列化,你收到的只是一堆"死"的文本字符串,程序无法对其进行逻辑判断、计算或存储。
1. 恢复"行为能力"
- 现状 :当你收到一个 JSON 字符串(例如
{"name": "张三", "age": 18})时,它在 Java 眼里只是一个String类型。- 你不能 调用它的方法(如
user.sayHello())。 - 你不能 直接访问它的属性(如
user.getAge()会报错,因为它是字符串,不是对象)。 - 你不能 把它传给需要
User类型参数的方法。
- 你不能 调用它的方法(如
- 反序列化的作用 :它根据字符串的内容,在内存中重新创建 一个真正的
User对象。- 一旦变成对象,它就"活"了!你可以调用它的业务逻辑、验证它的数据、让它执行任务。
2. 类型安全与代码规范
-
现状 :如果一直操作 JSON 字符串或
Map,你需要手动解析每个字段,还要自己判断类型(这是数字还是字符串?)。// ❌ 不反序列化,手动解析字符串(痛苦且易错) String json = "{\"age\": \"18\"}"; // 你得自己截取字符串,还得担心 "18" 是字符串还是数字,万一写错代码就崩了 int age = Integer.parseInt(extractValue(json, "age")); -
反序列化的作用 :直接转成强类型的 Java 对象。
// ✅ 反序列化后(安全且智能) User user = JSONUtil.toBean(json, User.class); int age = user.getAge(); // 编译器自动检查类型,如果是字符串会自动尝试转换,代码清晰 if (age > 18) { ... } // 享受智能提示和自动补全 -
好处 :利用 Java 的静态类型检查,在编译阶段就能发现错误,而不是等到程序运行崩溃。
3. 业务逻辑的入口
在后端开发中,90% 的场景都是:接收请求 -> 反序列化 -> 处理业务 -> 序列化 -> 返回响应。
- 场景举例 :用户注册接口。
- 前端发来 JSON:
{"username": "admin", "password": "123456"}。 - 反序列化 :后端将其转为
RegisterRequest对象。 - 业务处理 :
- 调用
request.getUsername()检查用户名是否已存在。 - 调用
PasswordEncoder.encode(request.getPassword())加密密码。 - 调用
userService.save(request)存入数据库。
- 调用
- 如果没有反序列化,上述所有步骤都无法用面向对象的方式优雅地完成,代码会变成一堆字符串切割和正则匹配,难以维护。
- 前端发来 JSON:
4. 数据清洗与校验(自动过滤脏数据)
- 现状:外部传来的 JSON 可能包含多余字段、格式错误或恶意数据。
- 反序列化的作用 :在转换过程中,框架可以自动进行数据绑定和校验 。
- 类型转换 :JSON 里的
"18"(字符串) 自动变成 Java 里的18(int)。 - 忽略多余字段 :如果 JSON 里多了一个前端传错的字段
{"hack": "..."},反序列化到User对象时,因为User类里没有hack字段,它会被自动丢弃,保护了内部结构。 - 默认值填充 :如果 JSON 里缺了某个字段,Java 对象可以使用默认值(如
age默认为 0),防止空指针异常。
- 类型转换 :JSON 里的