前两天我在处理业务的时候遇到一个问题,需要在定义好的实体类中动态添加一些客户自定义的字段,序列化为 JSON 字符串返回给前端使用。

    比如定义了一个学生的实体类 Student,类中只有 id、name 两个字段:

    1. public class Student {
    2. private String id;
    3. private String name;
    4. // get set function
    5. }

    在学生的设置页面,用户可以根据需要添加一些自定义属性,比如 age、address 等,因为属性名不确定,所以我们也没办法提前在 Student 类中定义这些属性。

    在没有使用 Jackson 提供的特性之前,我们能想到的是在 Student 类中增加一个 Map 类型的字段来存放用户自定义的属性,写法如下所示。

    1. public class Student {
    2. private String id;
    3. private String name;
    4. private Map<String, String> customFields = new HashMap<>();
    5. // get set function
    6. }

    这样序列化后,返回给前端的 JSON 字符串的内容如下所示。

    1. {
    2. "id": "1",
    3. "name": "张三",
    4. "customFields": {
    5. "address": "suzhou",
    6. "age": "12"
    7. }
    8. }

    用户自定义的属性没有合并到 Student 对象的属性中,还是独立放在 customFields 字段中。我们希望给到前端的 JSON 数据内容如下所示。

    1. {
    2. "id": "1",
    3. "name": "张三",
    4. "address": "suzhou",
    5. "age": "12"
    6. }

    这就需要利用 Jackson 的一些特性了,它提供了两个注解可以完美的解决这个问题,@JsonAnyGetter & @JsonAnySetter,使用方法如下所示。

    1. public class Student {
    2. private String id;
    3. private String name;
    4. private Map<String, String> customFields = new HashMap<>();
    5. @JsonAnyGetter
    6. public Map<String, String> getCustomFields() {
    7. return customFields;
    8. }
    9. @JsonAnySetter
    10. public void setCustomFields(String key, String value) {
    11. this.customFields.put(key, value);
    12. }
    13. // get set function
    14. }

    在 getCustomFields 方法上添加 @JsonAnyGetter 注解,在 setCustomFields 方法上添加 @JsonAnySetter,这样不管是序列化为 JSON 字符串还是反序列化为 Java 对象都可以实现了。

    需要注意 setCustomFields 方法的参数,不能是 customFields 字段,因为前端传过来的 JSON 字符串中没有这个字段,所以只能提供两个参数 key 和 value,反序列化时,Jackson 会自动把自定义的属性添加到 customFields 字段中。

    测试方法如下所示。

    1. public static void main(String[] args) throws JsonProcessingException {
    2. Student student = new Student();
    3. student.setId("1");
    4. student.setName("张三");
    5. student.setCustomFields("age", "12");
    6. student.setCustomFields("address", "suzhou");
    7. String jsonStr = OBJECT_MAPPER.writeValueAsString(student);
    8. System.out.println(jsonStr);
    9. Student student1 = OBJECT_MAPPER.readValue(jsonStr, Student.class);
    10. Map<String, String> customFields = student1.getCustomFields();
    11. for(Map.Entry<String, String> entry : customFields.entrySet()) {
    12. System.out.println("key: "+ entry.getKey() + ", value: " + entry.getValue());
    13. }
    14. }

    参考:Jackson @JsonAnyGetter and @JsonAnySetter Example

    作者:殷建卫 链接:https://www.yuque.com/yinjianwei/vyrvkf/ptyd7w 来源:殷建卫 - 架构笔记 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。