Spring Boot玩轉JSON!解鎖Jackson的八大高階技能,太強了
環境:SpringBoot3.4.2
1. 簡介
本篇文章將介紹使用Jackson庫處理JSON數據的多種高級技術,涵蓋了JSON解析、查詢、序列化/反序列化控制等核心功能。主要實現了以下能力:
- JSON路徑查詢:通過findValue()和at()方法實現嵌套JSON結構的深度查詢
- 多值提取:支持批量獲取相同名稱的多個值
- 視圖控制:使用@JsonView實現字段級序列化控制,區分公開和內部視圖
- 動態屬性處理:通過@JsonAnySetter/Getter處理未知字段,實現靈活的動態對象
- 對象展開:使用@JsonUnwrapped將嵌套對象屬性扁平化到父級
- 原始JSON保留:通過@JsonRawValue保持字符串字段的原始JSON格式
這些功能組合構成了強大的JSON數據處理能力,適用于API開發、配置解析、數據轉換等場景,特別適合需要靈活處理復雜JSON結構的Java應用程序。
2.實戰案例
2.1 findValue查找值
Jackson中的findValue()方法允許我們在JSON樹中搜索特定鍵值并獲取其關聯的值。首先,我們將使用ObjectMapper將JSON字符串轉換為JsonNode,從而創建JSON數據的樹形表示:
@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) ;
}輸出結果
pack@gmail.com2.2 優雅處理缺失的鍵
在處理 JSON 時,我們可能會遇到鍵值缺失的情況。當 JSON 結構中找不到指定鍵時,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) ;
}輸出結果
null在此示例中,findValue("email") 返回 null,因為 JSON 中不存在 email 鍵。
2.3 使用 findValues() 方法處理數組
findValues方法查找指定名稱的JSON對象字段的方法——包括直接子值和后代值——并將找到的字段作為List返回。
@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) ;
}輸出結果
[pack@gmail.com, xg@qq.com]2.4 處理深度嵌套的key
與其在JSON結構中通過名稱查找鍵值,我們可使用at()方法定位深層嵌套JSON結構中特定路徑下的字段。如下示例:
@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) ;
}輸出結果
pack@gmail.com此處傳遞給 at() 方法的路徑是一個 JSON 指針,它是一種使用字符串語法遍歷 JSON 文檔的標準化方式。
2.5 使用@JsonView視圖控制字段輸出
用于指示由被注解的方法或字段所定義的屬性所屬視圖(一個或多個)的注解。示例注解如下:
@JsonView(BasicView.class)該注解指定,被注解的屬性在處理(序列化、反序列化)由BasicView.class(或其子類)標識的視圖時將被包含。如果包含多個視圖類標識符,則該屬性將屬于所有這些視圖。
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;
}測試用例
@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) ;
}輸出結果
{"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;
}
}測試用例
@Test
public void test6() throws Exception {
String json = """
{
"id": 666,
"name": "Pack_xg",
"age": 33,
"details": {
"phone": "18999999999",
"addr": "中國"
}
}
""";
ObjectMapper objectMapper = new ObjectMapper() ;
// 反序列化:未知字段會被 @JsonAnySetter 捕獲
DynamicObject user = objectMapper.readValue(json, DynamicObject.class);
System.out.println(user);
// 序列化:properties 中的內容會被 @JsonAnyGetter 寫回 JSON
String serializedJson = objectMapper.writeValueAsString(user);
System.out.println("\n重新序列化后的 JSON:");
System.out.println(serializedJson);
}輸出結果

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
































