Spring Boot玩轉(zhuǎn)JSON!解鎖Jackson的八大高階技能,太強(qiáng)了
環(huán)境:SpringBoot3.4.2
1. 簡(jiǎn)介
本篇文章將介紹使用Jackson庫(kù)處理JSON數(shù)據(jù)的多種高級(jí)技術(shù),涵蓋了JSON解析、查詢、序列化/反序列化控制等核心功能。主要實(shí)現(xiàn)了以下能力:
- JSON路徑查詢:通過(guò)findValue()和at()方法實(shí)現(xiàn)嵌套JSON結(jié)構(gòu)的深度查詢
- 多值提?。褐С峙揩@取相同名稱的多個(gè)值
- 視圖控制:使用@JsonView實(shí)現(xiàn)字段級(jí)序列化控制,區(qū)分公開和內(nèi)部視圖
- 動(dòng)態(tài)屬性處理:通過(guò)@JsonAnySetter/Getter處理未知字段,實(shí)現(xiàn)靈活的動(dòng)態(tài)對(duì)象
- 對(duì)象展開:使用@JsonUnwrapped將嵌套對(duì)象屬性扁平化到父級(jí)
- 原始JSON保留:通過(guò)@JsonRawValue保持字符串字段的原始JSON格式
這些功能組合構(gòu)成了強(qiáng)大的JSON數(shù)據(jù)處理能力,適用于API開發(fā)、配置解析、數(shù)據(jù)轉(zhuǎn)換等場(chǎng)景,特別適合需要靈活處理復(fù)雜JSON結(jié)構(gòu)的Java應(yīng)用程序。
2.實(shí)戰(zhàn)案例
2.1 findValue查找值
Jackson中的findValue()方法允許我們?cè)贘SON樹中搜索特定鍵值并獲取其關(guān)聯(lián)的值。首先,我們將使用ObjectMapper將JSON字符串轉(zhuǎn)換為JsonNode,從而創(chuàng)建JSON數(shù)據(jù)的樹形表示:
@Test
public void test1() throws Exception {
String json = """
{
"user": {
"id": 1,
"name": "Pack_xg",
"details": {
"email": "pack@gmail.com",
"phone": "18999999999"
}
}
}
""" ;
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(json) ;
String email = rootNode.findValue("email").asText();
System.err.println(email) ;
}輸出結(jié)果
pack@gmail.com2.2 優(yōu)雅處理缺失的鍵
在處理 JSON 時(shí),我們可能會(huì)遇到鍵值缺失的情況。當(dāng) JSON 結(jié)構(gòu)中找不到指定鍵時(shí),findValue() 方法將返回 null。如下示例:
@Test
public void test2() throws Exception {
String json = """
{
"user": {
"id": 1,
"name": "Pack_xg",
"details": {
"phone": "18999999999"
}
}
}
""" ;
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(json);
JsonNode emailNode = rootNode.findValue("email");
System.err.println(emailNode) ;
}輸出結(jié)果
null在此示例中,findValue("email") 返回 null,因?yàn)?JSON 中不存在 email 鍵。
2.3 使用 findValues() 方法處理數(shù)組
findValues方法查找指定名稱的JSON對(duì)象字段的方法——包括直接子值和后代值——并將找到的字段作為L(zhǎng)ist返回。
@Test
public void test3() throws Exception {
String json = """
{
"users": [
{ "id": 1, "name": "pack", "details": { "email": "pack@gmail.com" } },
{ "id": 2, "name": "xg", "details": { "email": "xg@qq.com" } }
]
}
""";
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(json);
List<String> emails = rootNode.findValues("email")
.stream()
.map(JsonNode::asText)
.toList() ;
System.err.println(emails) ;
}輸出結(jié)果
[pack@gmail.com, xg@qq.com]2.4 處理深度嵌套的key
與其在JSON結(jié)構(gòu)中通過(guò)名稱查找鍵值,我們可使用at()方法定位深層嵌套JSON結(jié)構(gòu)中特定路徑下的字段。如下示例:
@Test
public void test4() throws Exception {
String json = """
{
"company": {
"dept": {
"team": {
"lead": {
"name": "Pack_xg",
"details": {
"email": "pack@gmail.com"
}
}
}
}
}
}
""" ;
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(json);
String email = rootNode.at("/company/dept/team/lead/details/email").asText() ;
System.err.println(email) ;
}輸出結(jié)果
pack@gmail.com此處傳遞給 at() 方法的路徑是一個(gè) JSON 指針,它是一種使用字符串語(yǔ)法遍歷 JSON 文檔的標(biāo)準(zhǔn)化方式。
2.5 使用@JsonView視圖控制字段輸出
用于指示由被注解的方法或字段所定義的屬性所屬視圖(一個(gè)或多個(gè))的注解。示例注解如下:
@JsonView(BasicView.class)該注解指定,被注解的屬性在處理(序列化、反序列化)由BasicView.class(或其子類)標(biāo)識(shí)的視圖時(shí)將被包含。如果包含多個(gè)視圖類標(biāo)識(shí)符,則該屬性將屬于所有這些視圖。
public class User {
public interface PublicView {}
public interface InternalView extends PublicView {}
@JsonView(PublicView.class)
private String name;
@JsonView(InternalView.class)
private String email;
@JsonView(InternalView.class)
private String password;
}測(cè)試用例
@Test
public void test5() throws Exception {
User user = new User("Pack_xg", "pack@gmail.com", "123456") ;
// 使用
ObjectMapper mapper = new ObjectMapper();
// 只輸出 public 字段
String publicJson = mapper
.writerWithView(User.PublicView.class)
.writeValueAsString(user);
System.err.println(publicJson) ;
// 輸出所有字段
String internalJson = mapper
.writerWithView(User.InternalView.class)
.writeValueAsString(user);
System.err.println(internalJson) ;
}輸出結(jié)果
{"name":"Pack_xg"}
{"name":"Pack_xg","email":"pack@gmail.com","password":"123456"}2.6 處理未知屬性
@JsonAnySetter / @JsonAnyGetter 用于處理 JSON 中可能存在的額外字段,避免反序列化失敗。如下示例:
public class DynamicObject {
private Long id ;
private String name ;
private Map<String, Object> properties = new HashMap<>();
// ...
@JsonAnySetter
public void set(String name, Object value) {
properties.put(name, value);
}
@JsonAnyGetter
public Map<String, Object> getProperties() {
return properties;
}
}測(cè)試用例
@Test
public void test6() throws Exception {
String json = """
{
"id": 666,
"name": "Pack_xg",
"age": 33,
"details": {
"phone": "18999999999",
"addr": "中國(guó)"
}
}
""";
ObjectMapper objectMapper = new ObjectMapper() ;
// 反序列化:未知字段會(huì)被 @JsonAnySetter 捕獲
DynamicObject user = objectMapper.readValue(json, DynamicObject.class);
System.out.println(user);
// 序列化:properties 中的內(nèi)容會(huì)被 @JsonAnyGetter 寫回 JSON
String serializedJson = objectMapper.writeValueAsString(user);
System.out.println("\n重新序列化后的 JSON:");
System.out.println(serializedJson);
}輸出結(jié)果

2.7 扁平化嵌套對(duì)象
將嵌套對(duì)象的字段“展開”到外層。
public class Order {
private Long id ;
private String orderNo ;
@JsonUnwrapped
private Address address ;
}
public class Address {
private String provice;
private String city ;
private String county ;
}測(cè)試用例
@Test
public void test7() throws Exception {
Order order = new Order(1L, "XP-00001", new Address("新疆", "烏魯木齊", "天山區(qū)")) ;
ObjectMapper objectMapper = new ObjectMapper() ;
System.err.println(objectMapper.writeValueAsString(order)) ;
}輸出結(jié)果
{
"id" : 1,
"orderNo" : "XP-00001",
"provice" : "新疆",
"city" : "烏魯木齊",
"county" : "天山區(qū)"
}2.8 插入原始JSON
@JsonRawValue將一個(gè)字符串字段的內(nèi)容直接作為原始 JSON 片段寫入最終的 JSON 輸出中,而不是將其轉(zhuǎn)義為字符串。這在你需要嵌入已生成的 JSON 或動(dòng)態(tài) JSON 結(jié)構(gòu)時(shí)非常有用。如下示例:
public class Product {
private String name ;
private BigDecimal price ;
/**json字符串內(nèi)容*/
@JsonRawValue
private String details ;
}測(cè)試用例
@Test
public void test7() throws Exception {
Order order = new Order(1L, "XP-00001", new Address("新疆", "烏魯木齊", "天山區(qū)")) ;
ObjectMapper objectMapper = new ObjectMapper() ;
objectMapper.enable(SerializationFeature.INDENT_OUTPUT) ;
System.err.println(objectMapper.writeValueAsString(order)) ;
}輸出結(jié)果
{
"name" : "Spring Boot3實(shí)戰(zhàn)案例200講",
"price" : 70,
"details" : {"author": "pack_xg", "page_count": 1000}
}如果沒(méi)有@JsonRawValue注解,輸出結(jié)果如下:
{
"name" : "Spring Boot3實(shí)戰(zhàn)案例200講",
"price" : 70,
"details" : "{\"author\": \"pack_xg\", \"page_count\": 1000}"
}字符串被轉(zhuǎn)義了。

































