Jackson 2.x 系列【7】注解大全篇三

有道无术,术尚可求,有术无道,止于术。

本系列Jackson 版本 2.17.0

源码地址:https://gitee.com/pearl-organization/study-jaskson-demo

注解大全

接下来本篇会接着介绍完所有jackson-annotations模块提供的注解。

2.21 @JacksonAnnotation

@JacksonAnnotation是一个元注解(作用于其他注解的注解),它的主要作用是标识当前注解是Jackson提供的注解,将来也用于传递一些通用的注解配置。

@Target({ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface JacksonAnnotation {
}

Jackson提供的注解上都可以看到该注解:

在这里插入图片描述

2.22 @JacksonAnnotationsInside

@JacksonAnnotationsInside也是一个元注解,用于创建自定义组合注解。

@Target({ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JacksonAnnotationsInside {
}

2.23 @JsonMerge

@JsonMerge用于将新数据合并到POJOMap中,详细案例可以参考英文教程

@Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonMerge {
    OptBoolean value() default OptBoolean.TRUE;
}

示例,当前有一个对象,有一个Map属性:

class ObjectWithMap {
    String name;
    @JsonMerge
    Map<String, String> stringPairs;
    // Standard getters, setters and constructors
}

我们实例化对象并赋值:

        Map<String, String> stringStringMap = new HashMap<>();
        stringStringMap.put("field3", "value3");
        ObjectWithMap objectToUpdateWith = new ObjectWithMap("James", stringStringMap);

将新数据合并到该对象Map属性中:

        String newData = "{\"stringPairs\":{\"field1\":\"value1\",\"field2\":\"value2\"}}";
        ObjectWithMap update = objectMapper.readerForUpdating(objectToUpdateWith).readValue(newData);

输出结果如下:
在这里插入图片描述

2.24 @JsonAutoDetect

AutoDetect翻译过来是自动检测的意思,默认情况下,Jackson只对public类型的属性进行转换,如果不是public类型则需要提供getters/setter方法,如果不是public类型有又没有getters/setter时,可以使用 @JsonAutoDetect修改默认行为。

@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonAutoDetect {
	// getter可见性
    Visibility getterVisibility() default JsonAutoDetect.Visibility.DEFAULT;
	// isGetter可见性
    Visibility isGetterVisibility() default JsonAutoDetect.Visibility.DEFAULT;
	// setter可见性
    Visibility setterVisibility() default JsonAutoDetect.Visibility.DEFAULT;
	// 构造函数可见性
    Visibility creatorVisibility() default JsonAutoDetect.Visibility.DEFAULT;
 	// 字段可见性  
    Visibility fieldVisibility() default JsonAutoDetect.Visibility.DEFAULT;

可见性级别枚举类Visibility

    public static enum Visibility {
    	// 所有都可见
        ANY,
        // 不是私有的(不是 private 修饰)
        NON_PRIVATE,
        // protected、public修饰的
        PROTECTED_AND_PUBLIC,
        // 只被 public修饰的
        PUBLIC_ONLY,
        // 所有的都不可见
        NONE,
        // 默认的
        DEFAULT;
}

例如下面POJO类的属性都是private修饰的,而且没有getters/setter方法

public class AutoDetectUser {

    private Long id;

    private String name;

    private String idCard;

    private String phone;

    private String pwd;
}

当序列化时会报错:

Exception in thread "main" com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "id" (class com.pearl.jacksoncore.demo.databind.anno.AutoDetectUser), not marked as ignorable (0 known properties: ])
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 11] (through reference chain: com.pearl.jacksoncore.demo.databind.anno.AutoDetectUser["id"])

这是可以使用@JsonAutoDetect指定属性的可见性为ANY,表示不管属性的访问修饰符是啥,都可以进行序列化/反序列化:

@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
public class AutoDetectUser {}

2.25 @JsonFilter

@JsonFilter用于指定过滤器,通过过滤器可以过滤想要的属性、不想要的属性。

@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonFilter {
	// 指定过滤器名称
    String value();
}

示例,给某个类添加@JsonFilter注解,并指定过滤器的名称为myFilter

@JsonFilter("myFilter")
public class AutoDetectUser {
	//...........
}

ObjectMapper中添加一个过滤器,指定名称为myFilter,并设置过滤规则为只序列化namephone属性:

        // 添加一个过滤器,并命名为 myFilter
        SimpleFilterProvider filterProvider = new SimpleFilterProvider();
        // filterOutAllExcept : 序列化指定字段
        filterProvider.addFilter("myFilter", SimpleBeanPropertyFilter.filterOutAllExcept("name", "phone"));
        objectMapper.setFilterProvider(filterProvider);
        String jsonStrByJsonFilter = objectMapper.writeValueAsString(readUserByAutoDetectUser);
        System.out.println(jsonStrByJsonFilter);

输出结果:

{"name":"范冰冰","phone":"13896963636"}

2.26 @JsonBackReference、@JsonManagedReference

@JsonBackReference@JsonManagedReference是为了解决双向引用导致的无限递归问题,只能作用于对象类型的属性,不能是CollectionMap、数组或枚举,典型的应用场景是父子、兄弟关系的多个对象导致的循环依赖。

@Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonBackReference {
    String value() default "defaultReference";
}

@Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonManagedReference {
    String value() default "defaultReference";
}

例如当前有一个公司类和部门类,它们之间是父子关系,部门是公司的下属机构,公司包含了多个部门,部门中又包含了所属公司:

public class Dept {

    String name;

    Company company;
    // 省略.....
}    

public class Company {

    String name;

    List<Dept> deptList;
    // 省略.....
}

执行序列化:

        Company company=new Company();
        company.setName("杭州阿里巴巴");

        Dept dept001=new Dept();
        dept001.setName("研发部");
        dept001.setCompany(company);

        Dept dept002=new Dept();
        dept002.setName("人事部");
        dept002.setCompany(company);

        List<Dept> deptList=new ArrayList<>();
        deptList.add(dept001);
        deptList.add(dept002);
        company.setDeptList(deptList);

        String jsonStrByReference = objectMapper.writeValueAsString(company);
        System.out.println(jsonStrByReference);

当序列化公司对象中的部门对象时,由于部门又包含了公司对象,出现了无限递归调用,最终报错:

Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Document nesting depth (1001) exceeds the maximum allowed (1000, from `StreamWriteConstraints.getMaxNestingDepth()`) (through reference chain:  Document nesting depth (1001) exceeds the maximum allowed (1000, from `StreamWriteConstraints.getMaxNestingDepth()`) (through reference chain: com.pearl.jacksoncore.demo.databind.anno.Company["deptList"]->java.util.ArrayList[0]->com.pearl.jacksoncore.demo.databind.anno.Dept["company"]->com.pearl.jacksoncore.demo.databind.anno.Company["deptList"]->java.util.ArrayList[0]->com.pearl.jacksoncore.demo.databind.anno.Dept["company"]->com.pearl.jacksoncore.demo.databind.anno.Company["deptList"]->java.util.ArrayList[0]-
	at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:402)
	at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:361)
	at com.fasterxml.jackson.databind.ser.std.StdSerializer.wrapAndThrow(StdSerializer.java:323)
	at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:778)
	at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:183)
	at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:732)
	at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:770)

可以在子关系的对象中,也就是部门对象中依赖的公司属性上添加@JsonBackReference注解:

public class Dept {

    String name;

    @JsonBackReference
    Company company;
    // 省略.....
}

可以看到该注解的作用是不序列化存在循环依赖的属性,类似于@JsonIgnore

{"name":"杭州阿里巴巴","deptList":[{"name":"研发部"},{"name":"人事部"}]}

这时进行反序列化,部门中的公司对象会为空:
在这里插入图片描述
如果想要部门中引用的公司对象不为空,可以在父级对象中使用@JsonManagedReference注解:

public class Company {

    String name;

    @JsonManagedReference
    List<Dept> deptList;
    // 省略.....
}

再次执行,结果如下:

在这里插入图片描述
注意:还是需要在编码时,就解决循环依赖问题。

2.27 @JsonClassDescription、@JsonPropertyDescription

@JsonBackReference@JsonManagedReference用于给类和属性添加相关描述,类似于注释方便阅读,也可在序列化时,获取注解上的描述,用于打印日志说明啥的。

@Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@JacksonAnnotation
public @interface JsonPropertyDescription {
    String value() default "";
}
@JsonClassDescription("公司")
public class Company {

    @JsonPropertyDescription("公司名称")
    String name;
    // 省略.....
}

2.28 @JsonEnumDefaultValue

@JsonEnumDefaultValue用于指定在序列化时使用枚举默认值。

示例,当前有一个性别枚举类,设置MAN为默认值:

public enum GenderEnum {

    @JsonEnumDefaultValue
    MAN("man", "男"),

    WOMAN("woman", "女");

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

    private String code;

    @JsonValue
    private String desc;

   // 省略........
}

序列化时没有找到对应所有输出了默认值:

        objectMapper.enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE); // 开启序列化未知枚举值使用默认值
        GenderEnum genderEnum = objectMapper.readValue("\"人\"", GenderEnum.class);
        System.out.println(genderEnum); // MAN

2.29 @JsonIdentityInfo、@JsonIdentityReference

上面@JsonBackReference@JsonManagedReference主要解决父子、兄弟关系之间循环依赖问题,而@JsonIdentityInfo更加强大灵活,使用唯一标识来引用对象,适用于任何关系。

@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonIdentityInfo {
	// 指定Object Id对象ID使用的是该类的哪一个属性
    String property() default "@id";
	// 对象ID生成器
    Class<? extends ObjectIdGenerator<?>> generator();
	// 对象ID生解析器
    Class<? extends ObjectIdResolver> resolver() default SimpleObjectIdResolver.class;
	// 作用域
    Class<?> scope() default Object.class;
}

例如上面案例中的问题可以使用@JsonIdentityInfo解决

@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "name")
public class Company {

    String name;
   // 省略........
}   

可以看到@JsonBackReferenceDept中被循环引用的对象Company序列化为设置的属性:

// @JsonBackReference、@JsonManagedReference 忽略了被循环引用的对象
{"name":"杭州阿里巴巴","deptList":[{"name":"研发部"},{"name":"人事部"}]}
{"name":"杭州阿里巴巴","address":"西湖","deptList":[{"name":"研发部","company":"杭州阿里巴巴"},{"name":"人事部","company":"杭州阿里巴巴"}]}

@JsonIdentityReference是一个可选注解,和@JsonIdentityInfo一起使用,可以设置将整个对象始终都序列化为唯一标识。

@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonIdentityReference {
    boolean alwaysAsId() default false;
}

Company类上添加注解:

@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "name")
@JsonIdentityReference(alwaysAsId = true)
public class Company {
    String name;
   // 省略........

整个对象都被序列化为了唯一标识:

"杭州阿里巴巴"

2.30 @JsonIncludeProperties

@JsonIncludeProperties用于在序列化和反序列化时只包含某些属性,其他属性则被忽略。

@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonIncludeProperties {
    String[] value() default {};
 	// 省略......
}   

例如下面代码表示只序列化和反序列化idname属性:

@JsonIncludeProperties({"id","name"})
public class LoginUser {
 	// 省略......
}

执行代码:

        String jsonStrByIncludeProperties = objectMapper.writeValueAsString(loginUser);
        System.out.println(jsonStrByIncludeProperties);

        String StrJsonIncludeProperties="{\"id\":null,\"name\":\"范冰冰\",\"idCard\":\"62226203625656698\",\"phone\":\"13896963636\",\"pwd\":\"123456\"}";
        LoginUser readUserByStrJsonIncludeProperties = objectMapper.readValue(StrJsonIncludeProperties, LoginUser.class);
        System.out.println(readUserByStrJsonIncludeProperties);

输出结果:

{"id":null,"name":"范冰冰"}
LoginUser{id=null, name='范冰冰', idCard='null', phone='null', pwd='null'}

2.31 @JsonRootName

@JsonRootName用于设置根对象名称。

@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonRootName {
    String value();

    String namespace() default "";
}

示例,一个普通的POJO序列化后是这样的:

{"id":1767798780627279333,"birthdayDate":"2024-03-29 09:04:36","birthdayLocalDateTime":"2024-03-29 09:04:36","name":"老三","userAge":25}

添加@JsonRootName并指定根对象名称为userInfo

@JsonRootName("userInfo")
public class UserInfo {
//...........
}

执行序列化/反序列:

        // 开启支持序列化/反序列化时,包装/解包根对象特征
        objectMapper.enable(SerializationFeature.WRAP_ROOT_VALUE);
        objectMapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);
        String jsonRootNameStr = objectMapper.writeValueAsString(user);
        System.out.println(jsonRootNameStr);

可以看到,在序列化时,最外层多了一个根对象包装属性:

{"userInfo":{"id":1767798780627279333,"birthdayDate":"2024-03-29 09:04:36","birthdayLocalDateTime":"2024-03-29 09:04:36","name":"老三","userAge":25,}}

2.32 @JsonRawValue

@JsonRawValue 标识当前字段或方法,应当不进行转义或引用,常用于JSON字符串或JS函数。

@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonRawValue {
	// 开启或关闭该注解
    boolean value() default true;
}

例如当前有一个测试类:

public class JsonRawVO {

    public  String json;
 	// 省略......
}

设置一个有转义字符的属性:

        JsonRawVO jsonRawVO=new JsonRawVO();
        jsonRawVO.setJson("{\"id\":null,\"name\":\"范冰冰\",\"idCard\":\"62226203625656698\",\"phone\":\"13896963636\",\"pwd\":\"123456\"}");
        String jsonStrByJsonRawVO = objectMapper.writeValueAsString(jsonRawVO);
        System.out.println(jsonStrByJsonRawVO);

执行序列化:

        JsonRawVO jsonRawVO=new JsonRawVO();
        jsonRawVO.setJson("{\"id\":null,\"name\":\"范冰冰\",\"idCard\":\"62226203625656698\",\"phone\":\"13896963636\",\"pwd\":\"123456\"}");
        String jsonStrByJsonRawVO = objectMapper.writeValueAsString(jsonRawVO);
        System.out.println(jsonStrByJsonRawVO);

输出结果仍然包含了转义字符:

{"json":"{\"id\":null,\"name\":\"范冰冰\",\"idCard\":\"62226203625656698\",\"phone\":\"13896963636\",\"pwd\":\"123456\"}"}

添加@JsonRawValue

    @JsonRawValue
    public  String json;

输出结果不再包含转义字符:

{"json":{"id":null,"name":"范冰冰","idCard":"62226203625656698","phone":"13896963636","pwd":"123456"}}

2.33 @JsonUnwrapped

@JsonUnwrapped序列化时进行扁平化处理,并且可以指定属性的前后缀。

@Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonUnwrapped {
    boolean enabled() default true;
	// 前缀
    String prefix() default "";
	// 后缀
    String suffix() default "";
}

例如当前对象的JSON格式如下,包含了一个子对象:

{
    "name":"范冰冰",
    "idCard":"明星",
    "child":{
        "name":"哈哈"
    }
}

给对象属性添加注解,并指定前后缀:

    @JsonUnwrapped(prefix = "start_",suffix = "_pp")
    private Child child;

输出如下:

{
    "name":"范冰冰",
    "idCard":"明星",
    "start_name_pp":"哈哈"
}

2.34 @JsonView

@JsonView用于在序列化和反序列化指定视图,不同的视图返回的结果不一样,类似于数据权限。

@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonView {
    Class<?>[] value() default {};
}

演示需求:普通管理员可以查看用户敏感信息(银行卡号、手机号),超级管理员在普通管理员的基础上还可以查看到非常敏感的信息(密码)。

创建分类视图,可以理解为使用Class类作为分类标识:

public interface UserInfoView {
    // 敏感信息(普通管理员可以看)
    interface SensitiveView{}
    // 非常敏感信息(超级管理员可以看)
    interface VerySensitiveView{}
}

使用@JsonView注解标识当前属性属于哪些视图:

public class LoginUser {

    private Long id;

    private String name;

    @JsonView({UserInfoView.SensitiveView.class,UserInfoView.VerySensitiveView.class})
    private String idCard;

    @JsonView({UserInfoView.SensitiveView.class,UserInfoView.VerySensitiveView.class})
    private String phone;

    @JsonView(UserInfoView.VerySensitiveView.class)
    private String pwd;
	// 省略......
}

执行序列化:

        LoginUser loginUser=new LoginUser();
        loginUser.setName("范冰冰");
        loginUser.setIdCard("62226203625656698");
        loginUser.setPhone("13896963636");
        loginUser.setPwd("123456");
        // 管理员 
        String jsonStrByViewSensitive = objectMapper.writerWithView(UserInfoView.SensitiveView.class).writeValueAsString(loginUser);
        System.out.println(jsonStrByViewSensitive);
        // 超级管理员
        String jsonStrByVerySensitiveView = objectMapper.writerWithView(UserInfoView.VerySensitiveView.class).writeValueAsString(loginUser);
        System.out.println(jsonStrByVerySensitiveView);

输出结果如下:

{"id":null,"name":"范冰冰","idCard":"62226203625656698","phone":"13896963636"}
{"id":null,"name":"范冰冰","idCard":"62226203625656698","phone":"13896963636","pwd":"123456"}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

墨 禹

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值