有道无术,术尚可求,有术无道,止于术。
本系列Jackson 版本 2.17.0
源码地址:https://gitee.com/pearl-organization/study-jaskson-demo
文章目录
- 注解大全
- 2.11 @JsonValue
- 2.12 @JsonKey
- 2.13 @JsonAnySetter
- 2.14 @JsonAnyGetter
- 2.15 @JacksonInject
- 2.16 @JsonCreator
- 2.17 @JsonTypeInfo
- 2.18 @JsonSubTypes
- 2.19 @JsonTypeName
- 2.20 @JsonTypeId
注解大全
2.11 @JsonValue
@JsonValue
用于将整个对象序列化为单个值,常用于只包含单个值的简单对象或枚举类型。Jackson
会忽略对象的其他属性和字段,只序列化由 @JsonValue
注解指定的方法或属性值。
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonValue {
boolean value() default true;
}
例如当前有个性别枚举类:
public enum GenderEnum {
MAN("man", "男"),
WOMAN("woman", "女");
GenderEnum(String code, String desc) {
this.code = code;
this.desc = desc;
}
private String code;
private String desc;
// 省略getter\serter.......
}
用户对象类包含了性别枚举类属性:
GenderEnum gender;
设置性别并进行序列化:
user.setGender(GenderEnum.WOMAN);
String jsonValuesStr = objectMapper.writeValueAsString(user);
System.out.println(jsonValuesStr);
输出时可以看到枚举类被序列化为WOMAN
,我们期望输出的是女:
{"gender":"WOMAN","userAge":25,"mobilePhone":"13899996666"}
在枚举类字段上添加@JsonValue
:
@JsonValue
private String desc;
再次执行,可以看到将整个枚举类序列化为了一个值:
{"gender":"女","userAge":25,"mobilePhone":"13899996666"}
2.12 @JsonKey
@JsonKey
也是将某个对象序列化为单个值,但是仅在实例作为Map
类型中的键进行序列化时有效。
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonKey {
boolean value() default true;
}
例如下方代码:
Org org1=new Org();
org1.setId(1765672919981232128L);
org1.setOrgName("阿里巴巴");
Org org2=new Org();
org2.setId(1772078846569590784L);
org2.setOrgName("支付宝");
Map<Org, String> map = new HashMap<>();
map.put(org1,"顶级机构");
map.put(org2,"下级机构");
String orgStr = objectMapper.writeValueAsString(map);
System.out.println(orgStr);
序列化后输出如下:
{"Org{id=1765672919981232128, orgName='null', address='null'}":"顶级机构","Org{id=1772078846569590784, orgName='null', address='null'}":"下级机构"}
在Org
属性上添加@JsonKey
注解,表示序列化该对象时,使用orgName
值作为Map
中的键:
@JsonKey
private String orgName;
输出如下:
{"阿里巴巴":"顶级机构","支付宝":"下级机构"}
2.13 @JsonAnySetter
@JsonAnySetter
用于反序列化时,将被忽略或无法反序列化的内容,统一存放在Map
集合中。
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonAnySetter {
boolean enabled() default true;
}
在UserInfo
中添加一个Map
属性,Setter
方法使用@JsonAnySetter
标识:
private Map<String,Object> collect =new HashMap<>();
@JsonAnySetter
public void setCollect(String key,Object value) {
this.collect.put(key,value);
}
JSON
中有一个idCard
属性是UserInfo
类没有的:
String jsonAnySetterStr="{\"idCard\":\"43268825255522222\",\"userSex\":\"男\",\"id\":1767798780627279873,\"username\":\"坤坤\",\"userAge\":18,\"org\":{\"id\":1699967647585800192,\"orgName\":\"阿里巴巴\",\"address\":\"浙江杭州\"},\"roleList\":null}";
UserInfo readUserByAnySetter = objectMapper.readValue(jsonAnySetterStr, UserInfo.class);
System.out.println(readUserByAnySetter);
这时idCard
会被存放到Map
中:
2.14 @JsonAnyGetter
@JsonAnyGetter
则用于在序列化时,将Map
集合中的内容以对象属性的方式进行输出。
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonAnyGetter {
boolean enabled() default true;
}
直接将上面@JsonAnySetter
反序列化的对象进行序列化时,和之前的JSON
内容不一致:
{"collect":{"idCard":"43268825255522222","id":1767798780627279873},"userAge":18,"userSex":"男","mobilePhone":null}
添加@JsonAnyGetter
:
@JsonAnyGetter
public Map<String, Object> getCollect() {
return collect;
}
输出结果如下:
{"mobilePhone":null,"idCard":"43268825255522222","id":1767798780627279873}
2.15 @JacksonInject
@JacksonInject
标识该属性值不是直接从JSON
数据中读取,而是通过注入的方式,根据ObjectMapper
的配置或其他机制提供。
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JacksonInject {
// 逻辑ID,根据该ID从配置或者其他环境中查询对应的值并注入
String value() default "";
OptBoolean useInput() default OptBoolean.DEFAULT;
// 省略........
}
应用场景:
- 依赖注入:在对象创建时,可能有一些值需要通过外部机制(如
Spring
框架的依赖注入)提供,而不是从JSON
中解析,这时可以使用@JacksonInject
来标记这些属性 - 默认值:有时我们可能希望为某些属性提供默认值,而这些默认值不是从
JSON
数据中读取的。使用@JacksonInject
可以确保在没有从JSON
中找到对应值的情况下使用这些默认值。 - 上下文相关值:当处理与特定上下文相关的数据时,可能需要将某些上下文信息注入到对象中。这些上下文信息可能不是
JSON
数据的一部分,而是由调用方或应用程序的其他部分提供的
添加@JacksonInject
注解:
@JacksonInject(value = "phone")
private String phone;
使用InjectableValues
注入默认值:
// 设置默认值
InjectableValues.Std injectableValues = new InjectableValues.Std();
injectableValues.addValue("phone","13788889999");
objectMapper.setInjectableValues(injectableValues);
// 反序列化
String jsonInjectStr="{}";
UserInject readUserByInject = objectMapper.readValue(jsonInjectStr, UserInject.class);
System.out.println(readUserByInject);
输出结果如下:
User{name='null', phone='13788889999'}
2.16 @JsonCreator
@JsonCreator
指定反序列化时构造对象实例使用的方法。
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonCreator {
// 构建者类型
Mode mode() default JsonCreator.Mode.DEFAULT;
public static enum Mode {
DEFAULT,
DELEGATING,
PROPERTIES,
DISABLED;
private Mode() {
}
}
}
添加无参、有参的构造方法:
public UserInject() {
System.out.println("无参构造");
}
public UserInject(String name, String phone) {
this.name = name;
this.phone = phone;
System.out.println("有参构造");
}
进行序列化:
String jsonCreatorStr="{\"phone\":\"13566666665\",\"name\":\"张三\"}";
UserInject readUserByCreator = objectMapper.readValue(jsonCreatorStr, UserInject.class);
System.out.println(readUserByCreator);
输出结果中,可以看到默认使用的是无参构造和setter
方法:
无参构造
setPhone
setName
User{name='张三', phone='13566666665'}
可以使用 @JsonCreator
指定使用的构造函数,使用@JsonProperty
指定属性映射关系:
@JsonCreator
public UserInject(@JsonProperty("name") String name, @JsonProperty(value = "phone") String phone) {
this.name = name;
this.phone = phone;
System.out.println("有参构造");
}
输出结果如下:
有参构造
User{name='张三', phone='13566666665'}
2.17 @JsonTypeInfo
@JsonTypeInfo
用于多态类型时指定子类类型,以便在反序列化时能够准确地重建原始对象类型。
@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonTypeInfo {
// 定义如何包含类型信息
Id use();
// 定义类型信息应该在哪里包含
As include() default JsonTypeInfo.As.PROPERTY;
// 当 use 设置为 Id.AS_PROPERTY 时,这个属性定义了包含类型信息的 JSON 属性的名称
String property() default "";
// 当无法确定具体的子类型时,使用的默认实现类
Class<?> defaultImpl() default JsonTypeInfo.class;
// 定义是否传递类型标识符值的属性
boolean visible() default false;
// 确定在其子类型的多态反序列化过程中是否应严格要求类型ID
OptBoolean requireTypeIdForSubtypes() default OptBoolean.DEFAULT;
例如,定义了一个抽象类Person
,它有两个类型Student
、Teacher
,并定义一个返回对象PersonVO
定义了属性为Person
实例集合:
public abstract class Person {
public String name;
// getters and setters
}
public class Student extends Person{
}
public class Teacher extends Person{
}
public class PersonVO {
List<Person> personList;
// getters and setters
}
执行序列化和反序列化操作:
Student student=new Student();
student.setName("坤坤童鞋");
Teacher teacher=new Teacher();
teacher.setName("悠上老师");
List<Person> personList=new ArrayList<>();
personList.add(student);
personList.add(teacher);
PersonVO personVO=new PersonVO();
personVO.setPersonList(personList);
String jsonStrByPerson = objectMapper.writeValueAsString(personVO);
System.out.println(jsonStrByPerson);
PersonVO readUserByPerson = objectMapper.readValue(jsonStrByPerson, PersonVO.class);
System.out.println(readUserByPerson);
反序列化时,直接调用抽象类Person
的构造导致报错(应该调用具体的实现类的构造):
{"personList":[{"name":"坤坤童鞋"},{"name":"悠上老师"}]}
Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.pearl.jacksoncore.demo.databind.anno.Person` (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 16] (through reference chain: com.pearl.jacksoncore.demo.databind.anno.PersonVO["personList"]->java.util.ArrayList[0])
在Person
类上添加@JsonTypeInfo
,property
配置表示使用@class
作为类型属性名,JsonTypeInfo.Id.CLASS
表示使用全限定类名作为类型值:
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
public abstract class Person {}
再次执行,可以看到序列化时,包含了一个对象的Class
类型属性键值对,所以在反序列时使用指定类型的构造,正确输出:
{"personList":[{"@class":"com.pearl.jacksoncore.demo.databind.anno.Student","name":"坤坤童鞋"},{"@class":"com.pearl.jacksoncore.demo.databind.anno.Teacher","name":"悠上老师"}]}
com.pearl.jacksoncore.demo.databind.anno.PersonVO@32eff876
2.18 @JsonSubTypes
@JsonSubTypes
用于指定用于多态类型时,指定子类的类型标识。
@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonSubTypes {
Type[] value();
boolean failOnRepeatedNames() default false;
下方示例中,@JsonTypeInfo
表示使用type
作为子类类型属性名,使用自定义名称作为子类类型值,@JsonSubTypes
指定Student
类的名称为stu
,Teacher
类的名称为stu
:
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = Student.class,name = "stu"),
@JsonSubTypes.Type(value = Teacher.class,name = "tea")
})
public abstract class Person {//......}
序列化后输出如下:
{"personList":[{"type":"stu","name":"坤坤童鞋"},{"type":"tea","name":"悠上老师"}]}
2.19 @JsonTypeName
@JsonTypeName
作用在类上,用于添加当前类的类型标识符,常用于配合@JsonTypeInfo
多态类型处理问题。
@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonTypeName {
String value() default "";
}
在Student
类上添加注解:
@JsonTypeName("student111111")
public class Student extends Person{//......}
可以看到在序列化后携带了配置的类型标识:
{"personList":[{"student111111":{"name":"坤坤童鞋"}},{"teacher":{"name":"悠上老师"}}]}
2.20 @JsonTypeId
除了使用@JsonTypeName
还可以使用@JsonTypeId
添加类型标识,可以添加在属性、方法、参数上。
@Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonTypeId {
}
示例代码:
public class Student extends Person{
@JsonTypeId
private String typeId;
// getters and setters
}
public class Student extends Person{
@JsonTypeId
private String typeId;
// getters and setters
}
```java
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
@JsonSubTypes({
@JsonSubTypes.Type(value = Student.class,name = "sut11111111"),
@JsonSubTypes.Type(value = Teacher.class,name = "tea1111111")
})
输出结果:
{"personList":[{"sut11111111":{"name":"坤坤童鞋"}},{"tea1111111":{"name":"悠上老师"}}]}