来源:juejin.cn/post/7496341504849641510

👉 欢迎加入小哈的星球,你将获得: 专属的项目实战(多个项目) / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新项目:《Spring AI 项目实战》正在更新中..., 基于 Spring AI + Spring Boot 3.x + JDK 21;

  • 《从零手撸:仿小红书(微服务架构)》 已完结,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17..., 点击查看项目介绍;演示地址:http://116.62.199.48:7070/

  • 《从零手撸:前后端分离博客项目(全栈开发)》 2期已完结,演示链接:http://116.62.199.48/;

  • 专栏阅读地址:https://www.quanxiaoha.com/column

截止目前,累计输出 100w+ 字,讲解图 4013+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,Spring Cloud Alibaba 等等,戳我加入学习,解锁全部项目,已有4000+小伙伴加入

图片

  • 开家杂货铺吧

    • @JsonAnySetter:随便放!你给啥我都能接

    • @JsonAnyGetter:随便拿!需要啥我都能给

  • 总结一下!


最近在处理一个 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 数据结构,比如配置项、参数列表、插件信息等。

👉 欢迎加入小哈的星球,你将获得: 专属的项目实战(多个项目) / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新项目:《Spring AI 项目实战》正在更新中..., 基于 Spring AI + Spring Boot 3.x + JDK 21;

  • 《从零手撸:仿小红书(微服务架构)》 已完结,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17..., 点击查看项目介绍;演示地址:http://116.62.199.48:7070/

  • 《从零手撸:前后端分离博客项目(全栈开发)》 2期已完结,演示链接:http://116.62.199.48/;

  • 专栏阅读地址:https://www.quanxiaoha.com/column

截止目前,累计输出 100w+ 字,讲解图 4013+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,Spring Cloud Alibaba 等等,戳我加入学习,解锁全部项目,已有4000+小伙伴加入

图片

图片

图片

1. 我的私密学习小圈子,从0到1手撸企业实战项目~
2. SpringBoot 挥别 spring.factories:老伙计退场,新方案真香!
3. Elasticsearch、Open Distro、OpenSearch 和 Easysearch 到底啥区别?
4. 天才用户取用户名为null,害我熬夜查到两点钟。。
最近面试BAT,整理一份面试资料《Java面试BATJ通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。
获取方式:点“在看”,关注公众号并回复 Java 领取,更多内容陆续奉上。
PS:因公众号平台更改了推送规则,如果不想错过内容,记得读完点一下“在看”,加个“星标”,这样每次新文章推送才会第一时间出现在你的订阅列表里。
点“在看”支持小哈呀,谢谢啦
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐