jackson @JsonAnyGetter @JsonAnySetter 使用说明

@JsonAnyGetter@JsonAnySetter 是 Jackson 库中两个用于处理动态或未定义属性的注解,非常适合处理字段不固定的 JSON 数据。下面我将为你详细解释它们的用法。

@JsonAnyGetter - 动态序列化属性

@JsonAnyGetter 注解用于序列化 过程,它能将一个 Map 中的所有键值对平铺 到 JSON 中,与对象原有的其他字段处于同一级别

核心要点与示例

  • 方法要求 :该注解通常标注在一个无参数、返回 Map 对象 的方法上。这个 Map 包含了所有你希望平铺序列化的动态属性。
  • 示例场景 :假设你有一个 User 类,它有一些固定字段(如 id, name),同时希望灵活地添加其他属性(如 address, age
java 复制代码
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import java.util.HashMap;
import java.util.Map;

public class User {
    private String id;
    private String name;
    private Map<String, Object> dynamicFields = new HashMap<>(); // 存储动态属性

    // 标准的getter和setter方法
    public String getId() { return id; }
    public void setId(String id) { this.id = id; }
    
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }

    @JsonAnyGetter // 关键注解:序列化时,dynamicFields Map中的所有条目将被平铺输出
    public Map<String, Object> getDynamicFields() {
        return dynamicFields;
    }

    // 一个用于向动态字段添加属性的方法
    public void addDynamicField(String key, Object value) {
        this.dynamicFields.put(key, value);
    }
}

使用这个类并序列化:

java 复制代码
User user = new User();
user.setId("10001");
user.setName("小明");
user.addDynamicField("address", "浙江省杭州市");
user.addDynamicField("age", 22);

ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(user);
System.out.println(json);

输出结果

java 复制代码
{
  "id": "10001",
  "name": "小明",
  "address": "浙江省杭州市",
  "age": 22
}

如你所见,addressage 这两个存储在 dynamicFields Map 中的属性,被平铺到了最终的 JSON 里,与 idname 同级

@JsonAnySetter - 动态反序列化属性

@JsonAnySetter 注解用于反序列化 过程。当 JSON 中包含未在 Java 类中明确定义的字段时,Jackson 会调用被此注解标注的方法,将这些未知的键值对收集起来

核心要点与示例

  • 方法要求 :该注解标注在一个接受两个参数(String key, Object value) 的方法上
  • 示例场景:在反序列化时,处理 JSON 中超出已知字段的额外属性。
java 复制代码
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.HashMap;
import java.util.Map;

public class User {
    private String id;
    private String name;
    private Map<String, Object> dynamicFields = new HashMap<>();

    // ... (固定字段的getter和setter同上)

    @JsonAnySetter // 关键注解:反序列化时,未知属性会通过此方法存入dynamicFields
    public void setDynamicField(String key, Object value) {
        this.dynamicFields.put(key, value);
    }

    public Map<String, Object> getDynamicFields() {
        return dynamicFields;
    }

    public static void main(String[] args) throws Exception {
        String json = "{\"id\":\"10001\",\"name\":\"小明\",\"address\":\"浙江省杭州市\",\"age\":22}";

        ObjectMapper mapper = new ObjectMapper();
        User user = mapper.readValue(json, User.class);

        System.out.println("ID: " + user.getId()); // ID: 10001
        System.out.println("Name: " + user.getName()); // Name: 小明
        System.out.println("Dynamic Fields: " + user.getDynamicFields()); 
        // Dynamic Fields: {address=浙江省杭州市, age=22}
    }
}

在这个例子中,JSON 里的 addressageUser 类中没有对应的明确定义,因此它们被 @JsonAnySetter 标注的方法捕获,并放入了 dynamicFields Map 中

组合使用与注意事项

  1. 组合使用 :你可以同时使用 @JsonAnyGetter@JsonAnySetter,让一个类能够完美地处理动态属性的序列化与反序列化
java 复制代码
public class FlexibleBean {
    private Map<String, Object> extraProps = new HashMap<>();
    
    @JsonAnyGetter
    public Map<String, Object> getExtraProps() { return extraProps; }
    
    @JsonAnySetter
    public void setExtraProps(String key, Object value) { this.extraProps.put(key, value); }
}
  1. 关于Kotlin :请注意,在Kotlin的data class中,如果属性在构造函数中声明,@JsonAnySetter可能无法直接用于不可变的val属性。一种解决方法是将其声明为类体内普通的var属性,或者在反序列化时使用可变Map
  2. 方法访问权限 :确保被注解的方法具有公共public)的访问权限,否则Jackson可能无法访问它们

总结

  • @JsonAnyGetter :让你的对象在序列化为JSON时,能将Map中的键值对作为平铺的同级字段输出。
  • @JsonAnySetter :让你的对象在从JSON反序列化时,能捕获所有未知字段,并将其存入Map。

这两个注解联手,为你提供了一种非常灵活的方式来处理那些结构不固定或者包含动态自定义字段的JSON数据

希望这些解释和示例能帮助你理解并使用这两个注解。如果你在处理更复杂的嵌套结构时遇到问题,Jackson也提供了如@JsonUnwrapped等其他注解,或许可以进一步探索。

相关推荐
Mintopia4 小时前
🚀 Next.js 16 新特性深度解析:当框架开始思考人生
前端·后端·全栈
Good kid.4 小时前
一键部署 Deepseek网页聊天系统(基于 Spring Boot + HTML 的本地对话系统)
spring boot·后端·html
半夏知半秋5 小时前
redis-哨兵模式配置整理
数据库·redis·笔记·后端·学习·lua·安全架构
草莓熊Lotso5 小时前
模板进阶:从非类型参数到分离编译,吃透 C++ 泛型编程的核心逻辑
linux·服务器·开发语言·c++·人工智能·笔记·后端
JaguarJack5 小时前
2025 年必须尝试的 5 个 Laravel 新特性
后端·php·laravel
程序员爱钓鱼5 小时前
Python编程实战 - Python基础入门 - 容器的常用操作与应用
后端·python·bpython
程序员爱钓鱼5 小时前
Python编程实战 - 函数与模块化编程 - 函数的定义与调用
前端·后端·python
欧阳码农5 小时前
使用AI生成的页面总是被一眼认出来怎么办?1分钟给你解决
前端·后端
IT_陈寒5 小时前
7个鲜为人知的JavaScript性能优化技巧,让你的应用提速50%!
前端·人工智能·后端