Json工具(一)- Jackson(续)
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 {
@Override
public 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;
// 年收入 Map
private 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;
}
@JsonAnySetter
public void setMap(String key, Object value) {
if (map == null) {
map = new HashMap<>();
}
map.put(key, value);
}
@JsonAnyGetter
public 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;
@JsonCreator
public 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;
@JsonCreator
public 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 {
@JsonRawValue
private 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);
// normal
String normalView = mapper.writerWithView(CompanyViews.Normal.class).writeValueAsString(staff);
System.out.format("Normal views\n%s\n", normalView);
// manager
String managerView = mapper.writerWithView(CompanyViews.Manager.class).writeValueAsString(staff);
System.out.format("Manager views\n%s\n", managerView);
// hr
String 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;
@JsonIgnore
private 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 {
@Override
public 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的工厂对象以获取JavaType
TypeFactory typeFactory = TypeFactory.defaultInstance();
// 获取Bodys<Person>对应的JavaType
this.javaType = typeFactory.constructParametricType(this.clazz, this.internal);
// 如果是List<Person>对应的JavaType,TypeFactory.defaultInstance().constructParametricType(List.class,Person.class);
}
public ObjAndArrayDeserializer() {
super(Object.class);
}
@Override
public 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,缓存了反序列化器,需要重启才会再走*/
@Override
public 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. 关闭资源