Java 如何序列化不固定的 JSON 字段
最近在處理一個 JSON 接口時,遇到這樣一種情況:返回的數據中包含一些我事先并不知道的字段,這些字段會根據上下文動態變化,沒辦法在 Java 類中提前寫死字段名。
起初我以為只能通過 Map 手動解析,但后來發現 Jackson 提供了 @JsonAnyGetter 和 @JsonAnySetter 這兩個注解,專門用來處理這種“動態屬性”。它們能讓我優雅地把未知字段收集起來或者序列化出去,不影響已知字段的正常處理。
不過我在使用過程中也有點疑惑,比如這兩個注解的用法順序有什么講究?有哪些坑需要避免?這種方式是不是適合所有動態字段的場景?
開家雜貨鋪吧
@JsonAnyGetter/@JsonAnySetter: 像開了一家靈活應對一切需求的「雜貨鋪」????
想象你是個 JSON 雜貨鋪老板,門口寫著招牌:“你有啥,我都能裝;你要啥,我都能配。”
你平時會備一些常規商品(字段),但總有顧客帶些奇怪需求來問:
- “老板,有沒有草莓味的牙膏?”
- “能不能加點冰塊到辣醬里?”
這些你事先沒在貨架上準備的“臨時需求”,你也得接單,對吧?
這時候你就需要一對“萬能架子”——也就是:
@JsonAnySetter:隨便放!你給啥我都能接
每次有奇怪字段進來(JSON 反序列化時),你就把它們統統放進一個萬能柜子(通常是 Map<String, Object>):
@JsonAnySetter
public void add(String key, Object value) {
otherProps.put(key, value);
}
比如這個 JSON:
{
"name": "豆瓣醬",
"spicy": true,
"limited_edition": "yes",
"extra_notes": "只在冬天賣"
}
你類里只定義了 name 和 spicy 字段,但 limited_edition 和 extra_notes 也能順利進貨,被收納進了 otherProps 這個萬能抽屜里。
@JsonAnySetter 用于標注一個方法,該方法可以接收 JSON 中沒有預定義的屬性。當 Jackson 反序列化 JSON 時,如果遇到未在 Java 類中顯式定義的字段,它會調用這個方法并將字段名和字段值作為參數傳遞給它。
當你在反序列化 JSON 時,不希望顯式定義所有的字段,或者 JSON 中包含了動態的屬性時,使用 @JsonAnySetter 可以自動將這些字段添加到一個 Map 或類似的結構中。
接下來用一個完整的代碼示例,我們來實現反序列化時動態添加屬性:
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.HashMap;
import java.util.Map;
publicclass Person {
private String name;
privateint age;
// 存儲額外的動態屬性
private Map<String, Object> additionalProperties = new HashMap<>();
// 添加動態屬性
@JsonAnySetter
public void addAdditionalProperty(String key, Object value) {
this.additionalProperties.put(key, value);
}
// 省略 getter 和 setter 方法
public Map<String, Object> getAdditionalProperties() {
return additionalProperties;
}
public static void main(String[] args) throws Exception {
String json = "{"name":"John","age":30,"address":"123 Street","nickname":"Johnny"}";
ObjectMapper mapper = new ObjectMapper();
Person person = mapper.readValue(json, Person.class);
System.out.println("Name: " + person.name); // 輸出:Name: John
System.out.println("Age: " + person.age); // 輸出:Age: 30
System.out.println("Additional Properties: " + person.getAdditionalProperties());
// 輸出:Additional Properties: {address=123 Street, nickname=Johnny}
}
}
在這個例子中:
- Person 類通過 @JsonAnySetter 注解的 addAdditionalProperty 方法來處理動態的屬性。
- Person 類中沒有顯式的字段來接收 address 和 nickname,但它們被添加到 additionalProperties 中。
- 當 Jackson 反序列化 JSON 時,它會把 address 和 nickname 作為動態屬性添加到 additionalProperties 中。
輸出:
Name: John
Age: 30
Additional Properties: {address=123 Street, nickname=Johnny}
@JsonAnyGetter:隨便拿!需要啥我都能給
到了序列化的時候,有顧客問你:“老板,這罐醬料里都包含什么成分?”
你就把主料(已有字段)和萬能抽屜里的額外信息一起打包給他,看起來就像全是標準字段一樣輸出!
@JsonAnyGetter
public Map<String, Object> getOtherProps() {
return otherProps;
}
這樣序列化輸出的 JSON 會自動把 otherProps 里的內容平鋪出去,和其他字段“融為一體”。
@JsonAnyGetter 用于標注一個方法,該方法返回一個 Map 或類似結構,它將包含對象的 動態屬性(即對象中沒有顯式定義的字段)。當 Jackson 序列化對象時,它會將這個 Map 中的鍵值對當作額外的 JSON 屬性來序列化。
當你有一個類,但是它可能會接受動態的字段,或者一些額外的鍵值對時,使用 @JsonAnyGetter 允許你將這些額外的字段序列化為 JSON。
繼續使用上面的person類,它有一些基本的屬性,但你希望允許動態添加額外的屬性,如額外的 "address" 、 "nickname" 等字段。
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.HashMap;
import java.util.Map;
publicclass Person {
private String name;
privateint age;
// 存儲額外的動態屬性
private Map<String, Object> additionalProperties = new HashMap<>();
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 通過該方法返回所有額外的動態屬性
@JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return additionalProperties;
}
public void addAdditionalProperty(String key, Object value) {
this.additionalProperties.put(key, value);
}
// 省略 getter 和 setter 方法
}
publicclass Main {
public static void main(String[] args) throws Exception {
Person person = new Person("John", 30);
person.addAdditionalProperty("address", "123 Street");
person.addAdditionalProperty("nickname", "Johnny");
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(person);
System.out.println(json); // 輸出:{"name":"John","age":30,"address":"123 Street","nickname":"Johnny"}
}
}
在這個例子中:
- Person 類包含一個 Map<String, Object> 來存儲動態屬性。
- 使用 @JsonAnyGetter 標注 getAdditionalProperties() 方法,表示 additionalProperties 中的鍵值對應該被序列化為 JSON 字段。
- 通過調用 addAdditionalProperty() 方法向 additionalProperties 中添加動態字段。
輸出:
{
"name": "John",
"age": 30,
"address": "123 Street",
"nickname": "Johnny"
}
總結一下!
圖片
特別適合那些字段不固定、可能需要動態擴展的 JSON 數據結構,比如配置項、參數列表、插件信息等。