最近在处理一个 JSON 接口时,遇到这样一种情况:返回的数据中包含一些我事先并不知道的字段,这些字段会根据上下文动态变化,没办法在 Java 类中提前写死字段名。起初我以为只能通过 Map 手动解析,但后来发现 Jackson 提供了
@JsonAnyGetter
和@JsonAnySetter
这两个注解,专门用来处理这种"动态属性"。它们能让我优雅地把未知字段收集起来或者序列化出去,不影响已知字段的正常处理。不过我在使用过程中也有点疑惑,比如这两个注解的用法顺序有什么讲究?有哪些坑需要避免?这种方式是不是适合所有动态字段的场景?
开家杂货铺吧
@JsonAnyGetter
/ @JsonAnySetter
:像开了一家灵活应对一切需求的「杂货铺」🧃🧂
想象你是个 JSON 杂货铺老板,门口写着招牌:"你有啥,我都能装;你要啥,我都能配。"
你平时会备一些常规商品(字段),但总有顾客带些奇怪需求来问:
- "老板,有没有草莓味的牙膏?"
- "能不能加点冰块到辣酱里?"
这些你事先没在货架上准备的"临时需求",你也得接单,对吧?
这时候你就需要一对"万能架子"------也就是:
@JsonAnySetter
:随便放!你给啥我都能接
每次有奇怪字段进来(JSON 反序列化时),你就把它们统统放进一个万能柜子(通常是 Map<String, Object>
):
java
@JsonAnySetter
public void add(String key, Object value) {
otherProps.put(key, value);
}
比如这个 JSON:
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
或类似的结构中。
接下来用一个完整的代码示例,我们来实现反序列化时动态添加属性:
java
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.HashMap;
import java.util.Map;
public class Person {
private String name;
private int 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
中。
输出:
text
Name: John
Age: 30
Additional Properties: {address=123 Street, nickname=Johnny}
@JsonAnyGetter
:随便拿!需要啥我都能给
到了序列化的时候,有顾客问你:"老板,这罐酱料里都包含什么成分?"
你就把主料(已有字段)和万能抽屉里的额外信息一起打包给他,看起来就像全是标准字段一样输出!
java
@JsonAnyGetter
public Map<String, Object> getOtherProps() {
return otherProps;
}
这样序列化输出的 JSON 会自动把 otherProps
里的内容平铺出去,和其他字段"融为一体"。
@JsonAnyGetter
用于标注一个方法,该方法返回一个Map
或类似结构,它将包含对象的 动态属性 (即对象中没有显式定义的字段)。当 Jackson 序列化对象时,它会将这个Map
中的键值对当作额外的 JSON 属性来序列化。当你有一个类,但是它可能会接受动态的字段,或者一些额外的键值对时,使用
@JsonAnyGetter
允许你将这些额外的字段序列化为 JSON。
继续使用上面的person类,它有一些基本的属性,但你希望允许动态添加额外的属性,如额外的 "address" 、 "nickname" 等字段。
java
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.HashMap;
import java.util.Map;
public class Person {
private String name;
private int 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 方法
}
public class 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
中添加动态字段。
输出:
text
{
"name": "John",
"age": 30,
"address": "123 Street",
"nickname": "Johnny"
}
总结一下!
注解 | 类比 | 用途 |
---|---|---|
@JsonAnySetter |
顾客带啥都能收的"万能抽屉"🗃️ | JSON → Java:把未知字段也收进去 |
@JsonAnyGetter |
万能抽屉变展示架,拿出来卖!🛒 | Java → JSON:把 Map 中的额外字段"当作正常字段"输出 |
特别适合那些字段不固定、可能需要动态扩展的 JSON 数据结构,比如配置项、参数列表、插件信息等。