5、Jackson常用注解
@JsonProperty
自定义属性在序列化和反序列化过程中所对应的JSON字段的名称,还可以指定是否序列化和反序列化。属性如下:
value:设置属性的名称。一般当JSON串中的key与对象中的属性名称不一致,比如分别采用了下划线命名与驼峰命名。
defaultValue:用于记录预期的默认值
required:定义在反序列化期间是否需要属性的值。
index:序列化时属性的顺序,默认为-1,越小越靠前。
access:更改序列化和反序列化中逻辑属性的可见性,A逻辑属性的可见性仅根据可见性和其他注释确定,access的默认值。
READ_ONLY 逻辑属性仅在序列化时可见(属性值参与序列化)、WRITE_ONLY 逻辑属性仅在反序列化时可见(属性值参与反序列化)、READ_WRITE 逻辑属性在序列化和反序列化时都可见
@JsonAlias
反序列化的时候可以让Bean的属性接收多个json字段的名称。
@JsonAutoDetect
控制序列化和反序列化过程中Java对象中访问修饰符的序列化策略。用于注解或者类上,默认为ANY。
@JsonIgnore
在json序列化时将java bean中的一些属性忽略掉,序列化和反序列化都受影响。一般标记在属性或者方法上,返回的json数据即不包含该属性。
@JsonIgnoreProperties
此注解是类注解,作用是json序列化时将java bean中的一些属性忽略掉,序列化和反序列化都受影响。
@JsonIgnoreType
该类作为别的类的属性时,该属性忽略序列化和反序列化。
@JsonInclude
是为实体类在接口序列化返回值时增加规则的注解
ALWAYS为默认值,表示全部序列化,即默认返回全部字段
NON_NULL表示值为null就不序列化,即值为null的字段不返回(当实例对象中有Optional或AtomicReferenceAtomicReferenceAtomicReference类型的成员变量时,如果Optional或AtomicReference引用的实例为null,用NON_NULL 不能使该字段不做序列化,此时应使用NON_ABSENT规则)
NON_ABSENT可在实例对象中有Optional或AtomicReference类型的成员变量时,如果Optional或AtomicReference引用的实例为null时,也可使该字段不做序列化,同时可以排除值为null的字段
NON_EMPTY排除字段值为null、空字符串、空集合、空数组、Optional类型引用为空,AtomicReference类型引用为空
NON_DEFAULT没有更改的字段不序列化,即未变动的字段不返回
CUSTOM:这个值要配合valueFilter属性一起使用,在序列化的时候会执行CustomFilter中的的equals方法,该方法的入参就是字段的值,如果equals方法返回true,字段就不会被序列化,如果equals方法返回false时字段才会被序列化,即true时不返回,false时才返回
@JsonInclude(value = JsonInclude.Include.CUSTOM, valueFilter = CustomFilter.class)
private String field;static class CustomFilter {@Overridepublic boolean equals(Object obj) {// 为null,或者不是字符串就返回true,即不返回该字段if (null == obj || !(obj instanceof String)) {return true;}// 长度大于2就返回true,意味着不被序列化return ((String) obj).length() > 2;}
}
@JsonFormat
是一个时间格式化注解,比如我们存储在mysql中的数据是date类型的,当我们读取出来封装在实体类中的时候,就会变成英文时间格式,而不是yyyy-MM-dd HH:mm:ss这样的中文时间,因此我们需要用到JsonFormat注解来格式化我们的时间。
@JsonUnwrapped
表明属性应该以扁平化的形式进行序列化,即目标属性将不会序列化为 JSON 对象,但其属性将序列化为包含它的对象的属性。
@JacksonInject
在使用JSON格式进行反序列化的时候,我们经常有这样一些需求。我们从客户端或者其他渠道获取了一个JSON格式的数据对象,该对象包含若干个属性。但是我们在将JSON字符串反序列化的时候,需要给它加上一些默认数据
@Data
public class PlayerStar {private String name;private Integer age;// 业余爱好,数组private String[] hobbies;// 朋友private List<String> friends;// 年收入 Mapprivate Map<String, BigDecimal> salary;@JacksonInject("responseTime")private LocalDateTime responseTime;
}public class App {public static void main(String[] args) throws Exception {ObjectMapper objectMapper = new ObjectMapper();// 为responseTime赋值为当前值InjectableValues.Std injectableValues = new InjectableValues.Std();injectableValues.addValue("responseTime", LocalDateTime.now());// 在反序列化过程中赋值给对象objectMapper.setInjectableValues(injectableValues);String jsonInString = "{\"name\":\"乔丹\",\"age\":45,\"hobbies\":[\"高尔夫球\",\"棒球\"]}";PlayerStar jordan = objectMapper.readValue(jsonInString, PlayerStar.class);System.out.println(jordan);}}
@JsonAnyGetter和@JsonAnySetter
@JsonAnySetter:可以将序列化时未识别到的属性都扔进一个map里。
@JsonAnyGetter:将map里的属性都平铺输出。
public class TestAnyGetterAndSetter {public static void main(String[] args) throws JsonProcessingException {String json1 = "{\"name\":\"zhangSan\",\"id\":1,\"address\":\"china\"}";ObjectMapper objectMapper = new ObjectMapper();Account account = objectMapper.readValue(json1, Account.class);System.out.println(account.getMap());String jsonString = objectMapper.writeValueAsString(account);System.out.println(jsonString);}}class Account {private long id;private String name;private Map<String, Object> map;public long getId() {return id;}public void setId(long id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}@JsonAnySetterpublic void setMap(String key, Object value) {if (map == null) {map = new HashMap<>();}map.put(key, value);}@JsonAnyGetterpublic Map<String, Object> getMap() {return map;}
}
@JsonSetter 和 @JsonGetter
@JsonSetter 和 @JsonGetter:@JsonSetter只能用于setter方法,@JsonGetter只能用户getter方法,只用二者中的一个标签时,与@JsonProperty作用一模一样。
@JsonCreator
用于构造方法或者静态方法上面,加了@JsonCreator注解,该类的对象在反序列化时,就走有@JsonCreator注解的方法来反序列化,原先无参+set的过程失效。
public class TestJsonCreator {public static void main(String[] args) throws JsonProcessingException {ObjectMapper objectMapper = new ObjectMapper();Page page = objectMapper.readValue("{" +" \"page_size\":12,\n" +" \"page_num\":1,\n" +" \"name\":\"Java\"\n" +"}", Page.class);System.out.println(page);}
}@Data
@AllArgsConstructor
class Page {@JsonProperty(value = "page_size")private int pageSize;@JsonProperty(value = "page_num")private int pageNum;private String name;@JsonCreatorpublic static Page unSerialize() {System.out.println("正在反序列化成对象");return new Page(12, 1, "Java");}
}
public class TestJsonCreator {public static void main(String[] args) throws JsonProcessingException {ObjectMapper objectMapper = new ObjectMapper();Page page = objectMapper.readValue("{" +" \"page_size\":12,\n" +" \"page_num\":1,\n" +" \"name\":\"Java\"\n" +"}", Page.class);System.out.println(page);}
}@Data
class Page {@JsonProperty(value = "page_size")private int pageSize;@JsonProperty(value = "page_num")private int pageNum;private String name;@JsonCreatorpublic Page(@JsonProperty("name") String name,@JsonProperty("page_num") int pageNum,@JsonProperty("page_size") int pageSize) {System.out.println("@JsonCreator生效");this.pageNum = pageNum;this.pageSize = pageSize;this.name = name;}
}
@JsonValue
用于get方法、属性字段,一个类只能用一个,加上这个注解时,该类的对象序列化时就会只返回这个字段的值做为序列化的结果。比如一个枚举类的get方法上加上该注解,那么在序列化这个枚举类的对象时,返回的就是枚举对象的这个属性,而不是这个枚举对象的序列化json串。
@JsonPropertyOrder
用于指定实体生成 json 时的属性顺序,一般用得不多
@JsonRawValue
能够按原样序列化属性。属性值不会被转义或者加引号(或者说,会自动去掉转义,多余的引号)。属性值已经是一个 JSON String,或者属性值已经被加了引号时很有用。
public class TestJsonRawValue {public static void main(String[] args) throws JsonProcessingException {News news = new News();news.setContent("\"中国No.1\"");news.setTitle("中国崛起!");news.setPublishTime(new Date());System.out.println("-- before serialization --");ObjectMapper objectMapper = new ObjectMapper();String jsonString = objectMapper.writeValueAsString(news);System.out.println(jsonString);News news1 = objectMapper.readValue(jsonString, News.class);System.out.println(news1);}
}@Data
class News {@JsonRawValueprivate String content;private String title;@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")private Date publishTime;
}
-- before serialization --
{"content":"\"中国No.1\"","title":"中国崛起!","publishTime":"2024-02-28 10:47:00"}
News(content="中国No.1", title=中国崛起!, publishTime=Wed Feb 28 10:47:00 CST 2024)-- after serialization --
{"content":"中国No.1","title":"中国崛起!","publishTime":"2024-02-28 10:47:41"}
News(content=中国No.1, title=中国崛起!, publishTime=Wed Feb 28 10:47:41 CST 2024)
@JsonRootName
用来指定root wrapper的名字。注意,只有当WRAP_ROOT_VALUE开启时,此注解才生效
@JsonSerialize
用于字段或get方法上,自定义序列化过程
@JsonDeserialize
用于字段或set方法上,自定义反序列化过程
@JacksonAnnotation
标注该注解是Jackson的注解,会被Jackson处理
@JacksonAnnotationsInside
自定义Jackson注解的组合注解
@JsonView
不同请求获取的对象的视图不同(属性数量不一致),使用@JsonView配合自定义视图实现
public class CompanyViews {public static class Normal {};public static class Manager extends Normal {};public static class HR extends Normal {};}
public class Staff {@JsonView(CompanyViews.Normal.class)private String name;@JsonView(CompanyViews.Normal.class)private int age;// two views@JsonView({CompanyViews.HR.class, CompanyViews.Manager.class})private String[] position;@JsonView(CompanyViews.Manager.class)private List<String> skills;@JsonView(CompanyViews.HR.class)private Map<String, BigDecimal> salary;
}
public class JacksonExample {public static void main(String[] args) {ObjectMapper mapper = new ObjectMapper();Staff staff = createStaff();try {mapper.enable(SerializationFeature.INDENT_OUTPUT);// normalString normalView = mapper.writerWithView(CompanyViews.Normal.class).writeValueAsString(staff);System.out.format("Normal views\n%s\n", normalView);// managerString managerView = mapper.writerWithView(CompanyViews.Manager.class).writeValueAsString(staff);System.out.format("Manager views\n%s\n", managerView);// hrString hrView = mapper.writerWithView(CompanyViews.HR.class).writeValueAsString(staff);System.out.format("HR views\n%s\n", hrView);} catch (IOException e) {e.printStackTrace();}}private static Staff createStaff() {Staff staff = new Staff();staff.setName("mkyong");staff.setAge(38);staff.setPosition(new String[]{"Founder", "CTO", "Writer"});Map<String, BigDecimal> salary = new HashMap() {{put("2010", new BigDecimal(10000));put("2012", new BigDecimal(12000));put("2018", new BigDecimal(14000));}};staff.setSalary(salary);staff.setSkills(Arrays.asList("java", "python", "node", "kotlin"));return staff;}}
Normal views
{"name" : "mkyong","age" : 38
}Manager views
{"name" : "mkyong","age" : 38,"position" : [ "Founder", "CTO", "Writer" ],"skills" : [ "java", "python", "node", "kotlin" ]
}HR views
{"name" : "mkyong","age" : 38,"position" : [ "Founder", "CTO", "Writer" ],"salary" : {"2018" : 14000,"2012" : 12000,"2010" : 10000}
}
6、自定义序列化和反序列化案例
自定义序列化工具的步骤
- 实现
JsonSerializer
自定义序列化逻辑 - 实现
JsonDeserializer
自定义反序列化逻辑
如需获取字段相关的其他元素信息需要实现ContextualDeserializer
接口获取上下文。然后获取字段的JavaType,Jackson提供了TypeFactory获取JavaType,调用ObjectCodec工具反序列化
没有泛型无需获取JavaType,直接使用readValue(String content, Class valueType) - 在需要处理的字段添加注解
/*案例相关entity*/
@Data
public class MyDto {@ObjAndArrayConverter(clazz = Bodys.class, internal = Person.class)@JsonProperty("bodys")private Bodys<Person> bodys;
}@Data
public class Bodys<T> {@JsonProperty("commonField")private String commonField;private T body;
}@Data
@JsonAutoDetect(getterVisibility = JsonAutoDetect.Visibility.ANY)
public class Person {@JsonProperty(value = "myName", index = 4)private String name;@JsonProperty(value = "myAge", index = 3)private Integer age;@JsonProperty(value = "birthday", index = 2)@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")@JsonInclude(JsonInclude.Include.NON_NULL)private Date date;@JsonProperty(value = "myHeight", index = 1)private int height;@JsonIgnoreprivate Action action;@JacksonInject(value = "responseTime")private Date responseTime;
}
/*自定义序列化工具*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@JacksonAnnotationsInside
@JsonDeserialize(using = ObjAndArrayDeserializer.class)
@JsonSerialize(using = ObjAndArraySerializer.class)
public @interface ObjAndArrayConverter {Class<?> clazz();Class<?> internal();
}/*自定义序列化处理逻辑*/
public class ObjAndArraySerializer extends JsonSerializer {@Overridepublic void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {gen.writeStartArray();gen.writeObject(value);gen.writeEndArray();}
}/*自定义反序列化处理逻辑*/
public class ObjAndArrayDeserializer extends StdDeserializer<Object> implements ContextualDeserializer {private Class<?> clazz;private Class<?> internal;/*** jackson 反序列化执行的时候需要知道当前的字段需要反序列化到哪个对象,需要获取对应的JavaType,如果没有泛型可以不获取JavaType。* 如果是Person的JavaType TypeFactory.defaultInstance().constructType(Person.class);可以不获取JavaType,直接使用readValue(String content, Class<T> valueType)*/private JavaType javaType;public ObjAndArrayDeserializer(Class<?> clazz, Class<?> internal) {super(clazz);this.clazz = clazz;this.internal = internal;// 获取JavaType的工厂对象以获取JavaTypeTypeFactory typeFactory = TypeFactory.defaultInstance();// 获取Bodys<Person>对应的JavaTypethis.javaType = typeFactory.constructParametricType(this.clazz, this.internal);// 如果是List<Person>对应的JavaType,TypeFactory.defaultInstance().constructParametricType(List.class,Person.class);}public ObjAndArrayDeserializer() {super(Object.class);}@Overridepublic Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException {JsonToken currentToken = p.getCurrentToken();if (currentToken == JsonToken.START_ARRAY) {p.nextToken();Object o = p.getCodec().readValue(p, this.javaType);p.nextToken();return o;} else {return null;}}/*获取Field的相关信息,以获取对应的JavaType,缓存了反序列化器,需要重启才会再走*/@Overridepublic JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException {ObjAndArrayConverter converter = property.getAnnotation(ObjAndArrayConverter.class);this.clazz = converter.clazz();this.internal = converter.internal();return new ObjAndArrayDeserializer(converter.clazz(), converter.internal());}
}
7、Jackson多态的序列化与反序列化
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME,include = JsonTypeInfo.As.EXISTING_PROPERTY,property = "type",visible = true
)
@JsonSubTypes(value = {@JsonSubTypes.Type(value = JacksonProduct1.class, name = "product1"),@JsonSubTypes.Type(value = JacksonProduct2.class, name = "product2")}
)
@Data
public abstract class JacksonParent {/*抽象的公有字段*/private String year = "2024";
}@Data
public class JacksonProduct1 extends JacksonParent {private String type = "product1";private String name;
}@Data
public class JacksonProduct2 extends JacksonParent {private String type = "product2";private String name;
}@Data
public class JacksonProduct {private List<JacksonParent> jacksonProduct;
}public static void main(String[] args) throws JsonProcessingException {ObjectMapper objectMapper = new ObjectMapper();JacksonProduct jacksonProduct = new JacksonProduct();JacksonProduct1 jacksonProduct1 = new JacksonProduct1();jacksonProduct1.setName("json1");JacksonProduct2 jacksonProduct2 = new JacksonProduct2();jacksonProduct2.setName("json2");List<JacksonParent> list = new ArrayList<>();list.add(jacksonProduct1);list.add(jacksonProduct2);jacksonProduct.setJacksonProduct(list);String s = objectMapper.writeValueAsString(jacksonProduct);System.out.println("序列化成字符串:" + s);// 序列化成字符串:{"jacksonProduct":[{"year":"2024","type":"product1","name":"json1"},{"year":"2024","type":"product2","name":"json2"}]}System.out.println("转换后的实体类结果:");System.out.println(objectMapper.readValue(s, JacksonProduct.class));// JacksonProduct(jacksonProduct=[JacksonProduct1(type=product1, name=json1), JacksonProduct2(type=product2, name=json2)])
}
8、Jackson序列化和反序列化执行流程
Jackson序列化流程
1. 获取相关的ObjectCodec
2. 初始化JsonGenerator
3. 获取DefaultSerializerProvider
4. 获取JsonSerializer
5. 开始序列化
6. 关闭资源
Jackson反序列化流程
1. 获取相关的ObjectCodec
2. 初始化JsonParser
3. DefaultDeserializationContext
4. 初始化JsonToken
5. 获取JsonDeserializer
6. 开始反序列化
7. 关闭资源