@JsonValue和@JsonCreator介绍

文章目录

你想了解Jackson中的 @JsonValue@JsonCreator注解的核心用法,这两个注解是Jackson中用于 自定义对象序列化/反序列化规则 的关键注解,常用来解决枚举、自定义对象的JSON转换问题,以下从定义、用法、场景对比等方面详细讲解,结合示例让你快速掌握。

核心概念先厘清

在讲解注解前,先明确两个基础概念:

  • 序列化(Serialization) :Java对象 → JSON字符串(如User对象 → {"name":"张三"});
  • 反序列化(Deserialization) :JSON字符串 → Java对象(如{"name":"张三"}User对象)。

@JsonValue负责序列化@JsonCreator负责反序列化,两者常配合使用。


一、@JsonValue 详解

1. 核心作用

标记在方法/字段上,指定该对象序列化时只输出此方法的返回值/此字段的值(而非对象的所有属性),实现自定义序列化规则。

2. 关键特性

  • 一个类中只能有一个 @JsonValue注解(多了会报错);
  • 标记在方法上时,方法不能有参数(getter方法风格);
  • 优先级高于默认序列化规则(覆盖Jackson默认的字段序列化)。

3. 典型使用场景

场景1:枚举类的自定义序列化(最常用)

默认枚举序列化会输出枚举名(如MALE),通过@JsonValue可自定义输出值(如数字/中文):

java 复制代码
import com.fasterxml.jackson.annotation.JsonValue;

public enum GenderEnum {
    MALE(1, "男"),
    FEMALE(2, "女");

    private final int code;
    private final String desc;

    GenderEnum(int code, String desc) {
        this.code = code;
        this.desc = desc;
    }

    // 序列化时,只输出desc的值(如"男"/"女")
    @JsonValue
    public String getDesc() {
        return desc;
    }

    // 若想序列化输出code,只需把@JsonValue移到这里
    // @JsonValue
    // public int getCode() {
    //     return code;
    // }

    // 常规getter
    public int getCode() {
        return code;
    }
}

测试序列化

java 复制代码
import com.fasterxml.jackson.databind.ObjectMapper;

public class Test {
    public static void main(String[] args) throws Exception {
        ObjectMapper objectMapper = new ObjectMapper();
        // 序列化结果:"男"(而非"MALE")
        String json = objectMapper.writeValueAsString(GenderEnum.MALE);
        System.out.println(json); 
    }
}
场景2:自定义对象的简化序列化

将复杂对象序列化为单一值(如用户对象序列化为用户名):

java 复制代码
import com.fasterxml.jackson.annotation.JsonValue;

public class User {
    private Long id;
    private String username;
    private Integer age;

    public User(Long id, String username, Integer age) {
        this.id = id;
        this.username = username;
        this.age = age;
    }

    // 序列化时,只输出username(而非{"id":1,"username":"张三","age":20})
    @JsonValue
    public String getUsername() {
        return username;
    }

    // 常规getter
    public Long getId() {
        return id;
    }

    public Integer getAge() {
        return age;
    }
}

测试序列化

java 复制代码
User user = new User(1L, "张三", 20);
// 序列化结果:"张三"(而非完整对象JSON)
String json = objectMapper.writeValueAsString(user);
System.out.println(json); 

二、@JsonCreator 详解

1. 核心作用

标记在构造方法/静态工厂方法上,指定反序列化时如何将JSON值转换为Java对象,解决自定义反序列化规则(尤其是枚举/不可变对象)。

2. 关键特性

  • 用于反序列化,与@JsonValue配合实现"自定义序列化→自定义反序列化"闭环;
  • 构造方法参数需用@JsonProperty指定JSON字段名(JDK 8+若编译时保留参数名可省略,但建议显式指定);
  • 支持单一值反序列化(如JSON字符串→枚举)、多字段反序列化(如JSON对象→自定义对象)。

3. 典型使用场景

场景1:枚举类的自定义反序列化(配合@JsonValue)

承接上面的GenderEnum,实现从"男/女"反序列化为枚举:

java 复制代码
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;

public enum GenderEnum {
    MALE(1, "男"),
    FEMALE(2, "女");

    private final int code;
    private final String desc;

    GenderEnum(int code, String desc) {
        this.code = code;
        this.desc = desc;
    }

    // 序列化:输出desc
    @JsonValue
    public String getDesc() {
        return desc;
    }

    // 反序列化:从desc值(如"男")转换为枚举
    @JsonCreator
    public static GenderEnum fromDesc(String desc) {
        for (GenderEnum gender : GenderEnum.values()) {
            if (gender.desc.equals(desc)) {
                return gender;
            }
        }
        throw new IllegalArgumentException("无效的性别描述:" + desc);
    }

    // 若需要从code反序列化,可新增方法:
    // @JsonCreator
    // public static GenderEnum fromCode(Integer code) {
    //     for (GenderEnum gender : GenderEnum.values()) {
    //         if (gender.code == code) {
    //             return gender;
    //         }
    //     }
    //     throw new IllegalArgumentException("无效的性别编码:" + code);
    // }

    public int getCode() {
        return code;
    }
}

测试反序列化

java 复制代码
// 从JSON字符串"男"反序列化为GenderEnum.MALE
GenderEnum gender = objectMapper.readValue("\"男\"", GenderEnum.class);
System.out.println(gender); // 输出:MALE
场景2:不可变对象的反序列化(构造方法标注)

不可变对象(字段为final)无法通过setter反序列化,需用@JsonCreator标记构造方法:

java 复制代码
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;

// 不可变用户对象(字段final,无setter)
public class ImmutableUser {
    private final Long id;
    private final String username;
    private final Integer age;

    // 反序列化时,通过此构造方法创建对象
    @JsonCreator
    public ImmutableUser(
            @JsonProperty("id") Long id, // 指定JSON字段名
            @JsonProperty("username") String username,
            @JsonProperty("age") Integer age) {
        this.id = id;
        this.username = username;
        this.age = age;
    }

    // 常规getter
    public Long getId() {
        return id;
    }

    public String getUsername() {
        return username;
    }

    public Integer getAge() {
        return age;
    }
}

测试反序列化

java 复制代码
String json = "{\"id\":1,\"username\":\"张三\",\"age\":20}";
ImmutableUser user = objectMapper.readValue(json, ImmutableUser.class);
System.out.println(user.getUsername()); // 输出:张三
场景3:静态工厂方法实现反序列化(替代构造方法)
java 复制代码
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;

public class Product {
    private Long id;
    private String name;
    private Double price;

    // 私有构造方法(禁止外部new)
    private Product(Long id, String name, Double price) {
        this.id = id;
        this.name = name;
        this.price = price;
    }

    // 静态工厂方法作为反序列化入口
    @JsonCreator
    public static Product create(
            @JsonProperty("id") Long id,
            @JsonProperty("name") String name,
            @JsonProperty("price") Double price) {
        // 可在此处添加参数校验逻辑
        if (price < 0) {
            throw new IllegalArgumentException("价格不能为负数");
        }
        return new Product(id, name, price);
    }

    // getter省略
}

三、@JsonValue + @JsonCreator 完整示例(枚举)

实现枚举的"编码序列化/解码反序列化"闭环:

java 复制代码
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;

public enum StatusEnum {
    SUCCESS(200, "成功"),
    FAIL(500, "失败");

    private final int code;
    private final String msg;

    StatusEnum(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    // 序列化:输出code(如200/500)
    @JsonValue
    public int getCode() {
        return code;
    }

    // 反序列化:从code(如200)转换为枚举
    @JsonCreator
    public static StatusEnum fromCode(Integer code) {
        for (StatusEnum status : StatusEnum.values()) {
            if (status.code == code) {
                return status;
            }
        }
        throw new IllegalArgumentException("无效的状态码:" + code);
    }

    public String getMsg() {
        return msg;
    }
}

测试闭环

java 复制代码
// 序列化:StatusEnum.SUCCESS → 200
String json = objectMapper.writeValueAsString(StatusEnum.SUCCESS);
System.out.println(json); // 输出:200

// 反序列化:200 → StatusEnum.SUCCESS
StatusEnum status = objectMapper.readValue("200", StatusEnum.class);
System.out.println(status.getMsg()); // 输出:成功

四、关键对比与注意事项

注解 作用阶段 标记位置 核心场景
@JsonValue 序列化(→JSON) 无参方法/字段 枚举自定义输出、对象简化序列化
@JsonCreator 反序列化(←JSON) 构造方法/静态工厂方法 枚举自定义解析、不可变对象反序列化

注意事项

  1. @JsonCreator的参数标注
    若构造方法/工厂方法的参数名与JSON字段名不一致,必须用@JsonProperty("字段名")指定,否则反序列化失败;
  2. 枚举的反序列化容错
    建议在@JsonCreator方法中处理非法值(如返回默认值/抛自定义异常),避免程序崩溃;
  3. 优先级
    @JsonValue/@JsonCreator的优先级高于Jackson的默认序列化/反序列化规则,也高于自定义Serializer/Deserializer(若同时配置);
  4. 空值处理
    反序列化时若JSON值为null,@JsonCreator方法需处理空值(如返回默认枚举),否则会抛空指针。

总结

  1. @JsonValue:控制Java对象序列化为JSON的格式,一个类仅能标注一个,常用于枚举自定义输出值;
  2. @JsonCreator :控制JSON反序列化为Java对象的逻辑,标记在构造方法/静态工厂方法上,需配合@JsonProperty指定参数映射;
  3. 核心配合场景:枚举类的自定义序列化(@JsonValue)+ 反序列化(@JsonCreator),实现JSON值与枚举的灵活转换;
  4. 扩展场景:不可变对象、复杂自定义对象的反序列化,替代传统的setter方式,更符合面向对象设计原则。

这两个注解是Jackson中处理自定义JSON转换的基础,尤其在枚举、不可变对象的场景中不可或缺,也是你在链路切换需求中处理参数/返回值JSON转换的常用工具。

相关推荐
这儿有一堆花10 小时前
JSON 与 MongoDB:直存对象的便利与隐性代价
数据库·mongodb·json
╰⋛⋋⊱⋋翅膀⋌⊰⋌⋚╯21 小时前
cJSON使用
json
正在走向自律1 天前
从Oracle到金仓KES:PL/SQL兼容性与高级JSON处理实战解析
数据库·sql·oracle·json·金仓数据库·电科金仓·兼容性挑战
皮卡龙2 天前
Java常用的JSON
java·开发语言·spring boot·json
@#---2 天前
如何准确判断json文件并且拿到我想要的信息
android·python·json
WarPigs2 天前
Unity添加Newtonsoft.json
json
张彦峰ZYF3 天前
巨大 JSON / 图结构数据架构层面选型:该放 Redis 还是 MongoDB?
redis·架构·json·巨大json/图结构架构选型·redis-mongodb
一颗不甘坠落的流星3 天前
【Antd】基于 Upload 组件,导入Json文件并转换为Json数据
前端·javascript·json
亮子AI3 天前
application/json 服务器收到的是字符串,还是json对象?
运维·服务器·json