作为一个学习Javaweb的新手,理解JSON的序列化和反序列化非常重要,因为它在现代Web开发,特别是Spring Boot中无处不在。
首先,我们简单了解一下JSON (JavaScript Object Notation)。
JSON 是一种轻量级的数据交换格式。它基于JavaScript编程语言的一个子集,但是独立于语言,也就是说,几乎所有的编程语言都有解析和生成JSON数据的库。
JSON 的主要特点:
易于人阅读和编写。
易于机器解析和生成。
常用于前后端之间的数据交换(比如浏览器和服务器之间)、API之间的数据传输。
基本结构是:
对象 (Object): 由键值对(key: value
)组成,键是字符串,值可以是字符串、数字、布尔值、数组、对象或 null
。对象用花括号 {}
包围。
{
"name": "张三",
"age": 30,
"isStudent": false
}
数组 (Array): 由有序的值(可以是任何JSON类型)组成。数组用方括号 []
包围。
[
"苹果",
"香蕉",
"橙子"
]
值 (Value): 字符串、数字、布尔值 (true
/false
)、null
、对象或数组。
概念: 将一个Java对象转换成JSON格式的字符串的过程,就叫做 JSON序列化。
为什么需要?
当你的Spring Boot应用需要将数据显示给前端(比如Web页面、移动App)或者发送给其他服务时,你需要将你的Java对象(比如一个表示用户的 User
对象,一个表示商品的 Product
对象)转换成一种标准格式,以便接收方能够理解。JSON就是最常用的标准格式之一。
想象一下:你的Java程序里有一个 Product
对象,它包含了 id
、name
、price
等属性。你不能直接把这个Java对象“发送”出去。你需要把它包装成一个字符串,这个字符串的格式就是JSON。
Java对象 (Product) ───────> JSON字符串
{ id: 1, name: "Laptop", price: 8000.0 } ─序列化─> '{"id":1,"name":"Laptop","price":8000.0}'
概念: 将一个JSON格式的字符串转换成对应的Java对象的过程,就叫做 JSON反序列化。
为什么需要?
当你的Spring Boot应用从前端(比如用户提交的表单数据、通过API接收到的请求体)或其他服务接收到数据时,这些数据通常是JSON格式的字符串。为了在你的Java程序中处理这些数据,你需要将这些JSON字符串转换成你可以操作的Java对象。
想象一下:前端通过一个表单提交了一个新商品的信息,数据以JSON字符串的形式发送到你的Spring Boot后端。你需要把这个JSON字符串转换回一个 Product
Java对象,这样你才能方便地访问 name
、price
等属性,并进行业务逻辑处理(比如保存到数据库)。
JSON字符串 ───────> Java对象
'{"name":"Keyboard","price":300.0}' ─反序列化─> Java对象 (Product: { name: "Keyboard", price: 300.0 })
这是Spring Boot的强大之处:它自动化地处理了大部分情况下的JSON序列化和反序列化!
核心依赖:
在你的pom.xml
文件中,如果你使用了spring-boot-starter-web
这个依赖,那么你就已经自动引入了处理JSON所需的所有库。
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
spring-boot-starter-web
依赖会间接地引入 spring-boot-starter-json
依赖,而 spring-boot-starter-json
默认使用的是 Jackson 这个库来处理JSON。
Jackson 库:
Jackson 是一个非常流行和强大的Java库,专门用于处理JSON。Spring Boot通过自动配置,将Jackson设置为默认的 HttpMessageConverter
。
HttpMessageConverter
是 Spring MVC 中的一个核心概念,它负责将HTTP请求体转换成Java对象(反序列化),以及将Java对象转换成HTTP响应体(序列化)。当Spring Boot检测到请求或响应涉及JSON时,它就会使用配置好的Jackson HttpMessageConverter
来完成转换。
自动化体现在哪里?
在你编写Spring Boot的Controller时,只需要使用特定的注解:
@RequestBody
): 当你的Controller方法的参数前面加上 @RequestBody
注解时,Spring Boot会自动尝试将HTTP请求体中的JSON字符串反序列化成这个参数对应的Java对象。@ResponseBody
或 @RestController
):
@RestController
注解(它是 @Controller
和 @ResponseBody
的组合),或者你的方法使用了 @ResponseBody
注解,Spring Boot会自动尝试将方法的返回值序列化成JSON字符串,并作为HTTP响应体发送出去。简单来说,Spring Boot + Jackson 让你在大多数情况下,只需要关注你的Java对象和Controller方法,而不需要手动编写JSON解析和生成的代码。
对于新手来说,学习这个概念以及Spring Boot如何处理它,至少有以下几个重要的用途:
HttpMessageNotReadableException
反序列化失败,或者序列化结果不符合预期)。理解底层机制可以帮助你更快地定位和解决问题。我们通过一个简单的例子来演示。
1. Maven 依赖 (确保 spring-boot-starter-web
在 pom.xml
中):
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>3.1.5version>
<relativePath/>
parent>
<groupId>com.examplegroupId>
<artifactId>json-demoartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>json-demoname>
<description>Demo project for JSON serialization/deserializationdescription>
<properties>
<java.version>17java.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
2. 创建一个简单的 Java 对象 (POJO):
这个对象将用于表示我们要在Java代码中操作的数据结构。Jackson在进行反序列化时,默认会寻找无参构造函数,以及通过getter/setter方法来匹配JSON字段和对象属性。为了简化,我们使用Lombok库来自动生成构造函数、getter/setter等。如果你不用Lombok,需要手动添加。
// Product.java
package com.example.jsondemo.model;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
@Data // 自动生成 getter, setter, toString, equals, hashCode
@NoArgsConstructor // 自动生成无参构造函数 (反序列化需要)
@AllArgsConstructor // 自动生成全参构造函数 (可选,方便创建对象)
public class Product {
private Long id;
private String name;
private double price;
// 如果不用 Lombok,你需要手动添加:
// public Product() {} // 无参构造函数
// public Product(Long id, String name, double price) { ... } // 全参构造函数
// public Long getId() { ... }
// public void setId(Long id) { ... }
// public String getName() { ... }
// public void setName(String name) { ... }
// public double getPrice() { ... }
// public void setPrice(double price) { ... }
}
注意:如果你没有使用Lombok,需要手动添加Lombok的Maven依赖并在IDE中安装Lombok插件。或者手动写出构造函数和getter/setter方法。
3. 创建一个 Spring Boot Controller:
这个Controller将演示如何接收JSON数据(反序列化)和返回JSON数据(序列化)。
// ProductController.java
package com.example.jsondemo.controller;
import com.example.jsondemo.model.Product;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
@RestController // @RestController 包含了 @Controller 和 @ResponseBody
@RequestMapping("/api/products")
public class ProductController {
// 模拟一个存储商品的列表 (实际应用中通常是数据库)
private List<Product> productList = new ArrayList<>();
// 用于生成唯一的ID
private AtomicLong nextId = new AtomicLong(1);
// === 演示 JSON 反序列化 ===
// 这个方法接收一个 POST 请求,请求体是 JSON 格式的 Product 对象
@PostMapping
public String createProduct(@RequestBody Product product) {
// @RequestBody: Spring Boot (通过 Jackson) 会自动把请求体中的 JSON 字符串
// 反序列化成一个 Product Java 对象,并赋值给 product 参数。
System.out.println("接收到产品信息:");
System.out.println("Name: " + product.getName());
System.out.println("Price: " + product.getPrice());
// 模拟保存商品并赋予ID
product.setId(nextId.getAndIncrement());
productList.add(product);
// 返回一个简单的字符串作为响应
return "产品已创建,ID为: " + product.getId();
}
// === 演示 JSON 序列化 ===
// 这个方法接收一个 GET 请求,并返回一个 Product 对象
@GetMapping("/{id}")
public Product getProductById(@PathVariable Long id) {
// Spring Boot (通过 Jackson) 会自动把返回的 Product Java 对象
// 序列化成 JSON 字符串,作为响应体发送出去。
// 模拟从列表中查找商品 (实际应用中是查询数据库)
for (Product product : productList) {
if (product.getId().equals(id)) {
System.out.println("正在返回产品信息 (将序列化为JSON): " + product);
return product; // 返回 Java 对象
}
}
// 如果找不到,返回 null (默认情况下 Jackson 会序列化为 'null')
// 更健壮的做法是抛出异常或返回 ResponseEntity with status 404
System.out.println("未找到ID为 " + id + " 的产品");
return null;
}
// 演示返回一个列表的序列化
@GetMapping
public List<Product> getAllProducts() {
// Spring Boot (通过 Jackson) 会自动把返回的 List 对象
// 序列化成 JSON 数组,作为响应体发送出去。
System.out.println("正在返回所有产品列表 (将序列化为JSON数组)");
return productList;
}
}
如何测试:
启动你的Spring Boot应用。
使用工具(如 Postman, cURL, Insomnia)进行测试:
测试反序列化 (POST请求):
URL: http://localhost:8080/api/products
(如果你的应用运行在默认端口8080)
Method: POST
Headers: Content-Type: application/json
Body: 选择 raw
并选择 JSON
,输入以下JSON:
{
"name": "Wireless Mouse",
"price": 25.50
}
发送请求。你应该在控制台看到打印的接收信息,并收到一个包含新产品ID的响应。
测试序列化 (GET请求):
假设上一步创建的产品ID是1。
URL: http://localhost:8080/api/products/1
Method: GET
发送请求。你应该收到一个包含该产品信息的JSON响应,类似这样:
{
"id": 1,
"name": "Wireless Mouse",
"price": 25.5
}
测试返回列表 (GET请求):
http://localhost:8080/api/products
GET
虽然自动化很方便,但有时你需要控制JSON的输出。Jackson提供了很多注解,可以在你的POJO类属性或Getter/Setter方法上使用:
@JsonIgnore
: 忽略某个属性,不进行序列化和反序列化。@JsonProperty("newFieldName")
: 改变属性在JSON中的字段名。@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
: 控制日期/时间字段的格式。@JsonInclude(JsonInclude.Include.NON_NULL)
: 在序列化时,如果属性值为null,则不包含该字段。@JsonCreator
, @JsonDeserialize
, @JsonSerialize
: 更高级的定制。这些注解让你能够精细地调整Java对象和JSON之间的映射关系。
spring-boot-starter-web
(它包含了 Jackson 库),Spring Boot 会自动配置 Jackson 作为默认的 HttpMessageConverter
。@RequestBody
(用于反序列化请求体) 和 @ResponseBody
(或使用 @RestController
,用于序列化返回值),Spring Boot 就会自动完成 JSON 的转换工作。希望这个详细的解释能帮助你这个新手更好地理解 Spring Boot 中的 JSON 处理!多动手实践,你会越来越熟悉的。