有道无术,术尚可求,有术无道,止于术。
本系列Jackson 版本 2.17.0
源码地址:https://gitee.com/pearl-organization/study-jaskson-demo
文章目录
- 注解大全
- 2.21 @JacksonAnnotation
- 2.22 @JacksonAnnotationsInside
- 2.23 @JsonMerge
- 2.24 @JsonAutoDetect
- 2.25 @JsonFilter
- 2.26 @JsonBackReference、@JsonManagedReference
- 2.27 @JsonClassDescription、@JsonPropertyDescription
- 2.28 @JsonEnumDefaultValue
- 2.29 @JsonIdentityInfo、@JsonIdentityReference
- 2.30 @JsonIncludeProperties
- 2.31 @JsonRootName
- 2.32 @JsonRawValue
- 2.33 @JsonUnwrapped
- 2.34 @JsonView
注解大全
接下来本篇会接着介绍完所有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
用于将新数据合并到POJO
或Map
中,详细案例可以参考英文教程。
@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
,并设置过滤规则为只序列化name
、phone
属性:
// 添加一个过滤器,并命名为 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
是为了解决双向引用导致的无限递归问题,只能作用于对象类型的属性,不能是Collection
、Map
、数组或枚举,典型的应用场景是父子、兄弟关系的多个对象导致的循环依赖。
@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;
// 省略........
}
可以看到@JsonBackReference
将Dept
中被循环引用的对象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 {};
// 省略......
}
例如下面代码表示只序列化和反序列化id
、name
属性:
@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"}