jackson序列化与反序列化的应用实践

准备序列化对象
准备了两个类, 教师类和学生类, 其中一个学生只有一个教师
这里省略了构造方法和setter, getter方法

Teacher.java

public class Teacher {
private String name;
private Integer age;
}
Student.java

package org.zwx;

public class Student {
private String name;
private Integer age;
private Sex sex;
private String fatherName;
private Date bornTime;
private Teacher teacher;
}
Sex.java

public enum Sex {
MALE(“男”), FEMALE(“女”);

private String name;

Sex(String name) {
    this.name = name;
}

public String getName() {
    return name;
}

}
4 引入jackson依赖
本示例是基于gradle的, 从maven中心仓库中选择了2.11.2版本的jackson-databind

compile group: ‘com.fasterxml.jackson.core’, name: ‘jackson-databind’, version: ‘2.11.2’
5 序列化与格式化输出
5.1 流程
首先需要有一个待序列化对象, 本例中的student对象
创建一个对象映射器, jackson包下的ObjectMapper
调用序列化函数, 本例中的writeValueAsString, 将对象转为字符串, 便于展示
5.2 代码
public void testSerializable() throws IOException {
Student student1 = new Student(“小明”, 18, Sex.MALE, “王富贵”, new Date(), new Teacher(“李老师”, 40));
Student student2 = new Student(“小花”, 16, Sex.FEMALE, “钱很多”, new Date(), new Teacher(“赵老师”, 38));
List students = new ArrayList<>();
students.add(student1);
students.add(student2);

ObjectMapper mapper = new ObjectMapper();
String s = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(students);
System.out.println(s);

}
5.3 结果
[ {
“name” : “小明”,
“age” : 18,
“sex” : “MALE”,
“fatherName” : “王富贵”,
“bornTime” : 1599996926917,
“teacher” : {
“name” : “李老师”,
“age” : 40
}
}, {
“name” : “小花”,
“age” : 16,
“sex” : “FEMALE”,
“fatherName” : “钱很多”,
“bornTime” : 1599996926917,
“teacher” : {
“name” : “赵老师”,
“age” : 38
}
} ]
5.4 分析
示例中调用了方法writerWithDefaultPrettyPrinter, 美化了json的格式
否则将打印[{“name”:“小明”,“age”:18,“sex”:“MALE”,“fatherName”:“王富贵”,“bornTime”:1599997061097,“teacher”:{“name”:“李老师”,“age”:40}},{“name”:“小花”,“age”:16,“sex”:“FEMALE”,“fatherName”:“钱很多”,“bornTime”:1599997061097,“teacher”:{“name”:“赵老师”,“age”:38}}]
6 自定义序列化的名字
6.1 场景
假如需要将序列化的json由驼峰命名修改为下划线命名, 如fatherName修改为father_name

只需要在字段fatherName上用注解JsonProperty配置

6.2 示例代码
@JsonProperty(“father_name”)
private String fatherName;
@JsonProperty(“born_time”)
private Date bornTime;
6.3 示例结果
[ {
“name” : “小明”,
“age” : 18,
“sex” : “MALE”,
“teacher” : {
“name” : “李老师”,
“age” : 40
},
“father_name” : “王富贵”,
“born_time” : 1599997157609
}, {
“name” : “小花”,
“age” : 16,
“sex” : “FEMALE”,
“teacher” : {
“name” : “赵老师”,
“age” : 38
},
“father_name” : “钱很多”,
“born_time” : 1599997157610
} ]
7 自定义输出格式
7.1 bornTime格式设置
当前bornTime的格式为unix时间戮, 可读性非常差

现修改为yyyy-MM-dd HH:mm:ss
并设置时区为东八区

示例代码

@JsonProperty(“born_time”)
@JsonFormat(pattern = “yyyy-MM-dd HH:mm:ss”, timezone = “GMT+8”)
private Date bornTime;
结果

[ {
“name” : “小明”,
“age” : 18,
“sex” : “MALE”,
“teacher” : {
“name” : “李老师”,
“age” : 40
},
“father_name” : “王富贵”,
“born_time” : “2020-09-13 19:50:47”
}, {
“name” : “小花”,
“age” : 16,
“sex” : “FEMALE”,
“teacher” : {
“name” : “赵老师”,
“age” : 38
},
“father_name” : “钱很多”,
“born_time” : “2020-09-13 19:50:47”
} ]
7.2 sex设置为中文
只需要为Sex添加一个方法getOrdinal, 并添加注解JsonValue即可

示例代码

@JsonValue
public String getOrdinal() {
return name;
}
示例结果

[ {
“name” : “小明”,
“age” : 18,
“sex” : “男”,
“teacher” : {
“name” : “李老师”,
“age” : 40
},
“father_name” : “王富贵”,
“born_time” : “2020-09-13 19:57:47”
}, {
“name” : “小花”,
“age” : 16,
“sex” : “女”,
“teacher” : {
“name” : “赵老师”,
“age” : 38
},
“father_name” : “钱很多”,
“born_time” : “2020-09-13 19:57:47”
} ]
7.3 sex设置为序号
有些场景喜欢用0和1等序号设置男女, 即枚举的序号: 0表示男, 1表示女

此时需要修改Set的getOrdinal方法

修改返回值类型为int
调用父类的getOrdinal方法
示例代码

@JsonValue
public int getOrdinal() {
return super.ordinal();
}
示例结果

[ {
“name” : “小明”,
“age” : 18,
“sex” : 0,
“teacher” : {
“name” : “李老师”,
“age” : 40
},
“father_name” : “王富贵”,
“born_time” : “2020-09-13 20:01:44”
}, {
“name” : “小花”,
“age” : 16,
“sex” : 1,
“teacher” : {
“name” : “赵老师”,
“age” : 38
},
“father_name” : “钱很多”,
“born_time” : “2020-09-13 20:01:44”
} ]
8 拍平嵌套类型
场景
如前面提到的结果所示, teacher的两个属性并不在student的第一层,
有时可能会更深的层次, 使用起来不太友好

如何用teacher_name和teacher_age两个属性代替teacher呢?

在Student的teacher属性上添加注解JsonUnwrapped, 意为不包裹
在Teacher的属性上利用注解JsonProperty重命名
示例代码
Student.java

@JsonUnwrapped
private Teacher teacher;
Teacher.java

@JsonProperty(“teacher_name”)
private String name;
@JsonProperty(“teacher_age”)
private Integer age;
示例结果
[ {
“name” : “小明”,
“age” : 18,
“sex” : 0,
“teacher_name” : “李老师”,
“teacher_age” : 40,
“father_name” : “王富贵”,
“born_time” : “2020-09-13 20:21:53”
}, {
“name” : “小花”,
“age” : 16,
“sex” : 1,
“teacher_name” : “赵老师”,
“teacher_age” : 38,
“father_name” : “钱很多”,
“born_time” : “2020-09-13 20:21:53”
} ]
9 自定义序列化器
9.1 场景
假如需要将年龄调整为理论学龄, 即将年龄减去7, 得到理论学龄, 如何操作呢?

创建自定义年龄序列化器AgeSerializer, 继承StdSerializer<>
创建AgeSerializer的构造方法
重写serialize函数
利用注解修指定Student属性age的序列化器AgeSerializer
9.2 示例代码
AgeSerializer.java

public class AgeSerializer extends StdSerializer {
protected AgeSerializer() {
super(Integer.class);
}

@Override
public void serialize(Integer value, JsonGenerator gen, SerializerProvider provider) throws IOException {
    gen.writeNumber(value - 7);
}

}
Student.java

@JsonSerialize(using = AgeSerializer.class)
private Integer age;
9.3 示例结果
[ {
“name” : “小明”,
“age” : 11,
“sex” : 0,
“teacher_name” : “李老师”,
“teacher_age” : 40,
“father_name” : “王富贵”,
“born_time” : “2020-09-13 20:31:59”
}, {
“name” : “小花”,
“age” : 9,
“sex” : 1,
“teacher_name” : “赵老师”,
“teacher_age” : 38,
“father_name” : “钱很多”,
“born_time” : “2020-09-13 20:31:59”
} ]
10 反序列化
10.1 流程
首先需要有序列化好的数据, 可以是string, byte[], 文件二进制等
创建一个对象映射器, jackson包下的ObjectMapper
调用反序列化函数, 本例中的readValue, 将字符串转为对象
10.2 反序列化对象数据
示例代码

public void testDeserializable() throws JsonProcessingException {
String s = “{“name”:“小明”,“age”:11,“sex”:0,“teacher_name”:“李老师”,“teacher_age”:40,“father_name”:“王富贵”,“born_time”:“2020-09-13 20:46:10”}”;
ObjectMapper mapper = new ObjectMapper();
Student student = mapper.readValue(s, Student.class);
System.out.println(student);
}
示例结果

Student{name=‘小明’, age=11, sex=MALE, fatherName=‘王富贵’, bornTime=Sun Sep 13 20:46:10 CST 2020, teacher=Teacher{name=‘李老师’, age=40}}
分析

为了便于打印对象数据, 重写了Student和Teacher的toString方法
从数据中可以看出, age的结果是错误的, 原因在于之前自定义的序列化器将年龄减小了7, 10.4节将会通过自定义反序列化器来解决此问题
10.3 反序列化对象数组数据
示例代码

public void testDeserializableStudents() throws JsonProcessingException {
String s = “[{“name”:“小明”,“age”:11,“sex”:0,“teacher_name”:“李老师”,“teacher_age”:40,“father_name”:“王富贵”,“born_time”:“2020-09-13 20:51:31”},{“name”:“小花”,“age”:9,“sex”:1,“teacher_name”:“赵老师”,“teacher_age”:38,“father_name”:“钱很多”,“born_time”:“2020-09-13 20:51:31”}]”;
ObjectMapper mapper = new ObjectMapper();
Student[] students = mapper.readValue(s, Student[].class);
for (Student student : students) {
System.out.println(student);
}
}
示例结果

Student{name=‘小明’, age=11, sex=MALE, fatherName=‘王富贵’, bornTime=Sun Sep 13 20:51:31 CST 2020, teacher=Teacher{name=‘李老师’, age=40}}
Student{name=‘小花’, age=9, sex=FEMALE, fatherName=‘钱很多’, bornTime=Sun Sep 13 20:51:31 CST 2020, teacher=Teacher{name=‘赵老师’, age=38}}
分析

readValue的第二个参数需要传类型, 这里推荐用数组, 不推荐用List, 具体原因笔者目前也没花时间去研究
10.4 自定义反序列化器
从10.2节及10.3的现象中可以看出来, 仅仅自定义的序列化器会导致序列化的过程是正常的, 反序列化的过程仍然是默认逻辑, 有时候会导致意想不到的结果

遇到此场景, 可以考虑自定义反序列化器

创建自定义反序列化器AgeDeserializer, 继承StdDeserializer<>
重写deserialize方法
在Student的age属性上添加注解JsonDeserialize, 并指定反序列化器AgeDeserializer
示例代码

AgeDeserializer.java

public class AgeDeserializer extends JsonDeserializer {
@Override
public Integer deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
return p.getIntValue() + 7;
}
}
Student.java

@JsonSerialize(using = AgeSerializer.class)
@JsonDeserialize(using = AgeDeserializer.class)
private Integer age;
示例结果

Student{name=‘小明’, age=18, sex=MALE, fatherName=‘王富贵’, bornTime=Sun Sep 13 20:51:31 CST 2020, teacher=Teacher{name=‘李老师’, age=40}}
Student{name=‘小花’, age=16, sex=FEMALE, fatherName=‘钱很多’, bornTime=Sun Sep 13 20:51:31 CST 2020, teacher=Teacher{name=‘赵老师’, age=38}}
11 注解JsonInclude
该注解使用在实体类上, 格式@JsonInclude(value = JsonInclude.Include.NON_DEFAULT)

其中, Include有7种参数, 功能对比如下

参数 功能 备注
Include.ALWAYS 属性总是序列化(需要有get方法) 默认值
Include.NON_DEFAULT 属性为默认值不序列化 如: int:0, bool:false
Include.NON_EMPTY 属性为空("")或null不序列化
Include.NON_NULL 属性为null不序列化
Include.CUSTOM
Include.USE_DEFAULTS
Include.NON_ABSENT
代码示例
Student.java

@JsonInclude(value = JsonInclude.Include.NON_DEFAULT)
public class Student {
public void testNonDefault() throws IOException {
Student student = new Student("", 0, null, null, null, null);
ObjectMapper mapper = new ObjectMapper();
String s = mapper.writeValueAsString(student);
System.out.println(s);
}
示例输出
{
“name” : “”,
“age” : -7
}
分析
龙华大道1号http://www.kinghill.cn/LongHuaDaDao1Hao/index.html

猜你喜欢

转载自blog.csdn.net/weixin_45032957/article/details/108575263