jackson序列化和反序列化的应用实践指南
源码地址:https://github.com/zhouweixin/serializable
1相关概念
序列化:把对象转换为字节序列的过程称为对象的序列化
反序列化:把字节序列恢复为对象的过程称为对象的反序列化
2序列化的作用
用于把内存中的对象状态保存到一个文件中或者数据库中
用于网络传送对象
用于远程调用传输对象
3准备序列化对象
准备了两个类,教师类和学生类,其中一个学生只有一个教师
这里省略了构造方法和setter,getter方法
Teacher.java
publicclassTeacher{ privateStringname; privateIntegerage; }
Student.java
packageorg.zwx; publicclassStudent{ privateStringname; privateIntegerage; privateSexsex; privateStringfatherName; privateDatebornTime; privateTeacherteacher; }
Sex.java
publicenumSex{ MALE("男"),FEMALE("女"); privateStringname; Sex(Stringname){ this.name=name; } publicStringgetName(){ returnname; } }
4引入jackson依赖
本示例是基于gradle的,从maven中心仓库中选择了2.11.2版本的jackson-databind
compilegroup:'com.fasterxml.jackson.core',name:'jackson-databind',version:'2.11.2'
5序列化与格式化输出
5.1流程
首先需要有一个待序列化对象,本例中的student对象
创建一个对象映射器,jackson包下的ObjectMapper
调用序列化函数,本例中的writeValueAsString,将对象转为字符串,便于展示
5.2代码
publicvoidtestSerializable()throwsIOException{ Studentstudent1=newStudent("小明",18,Sex.MALE,"王富贵",newDate(),newTeacher("李老师",40)); Studentstudent2=newStudent("小花",16,Sex.FEMALE,"钱很多",newDate(),newTeacher("赵老师",38)); Liststudents=newArrayList<>(); students.add(student1); students.add(student2); ObjectMappermapper=newObjectMapper(); Strings=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") privateStringfatherName; @JsonProperty("born_time") privateDatebornTime;
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.1bornTime格式设置
当前bornTime的格式为unix时间戮,可读性非常差
现修改为yyyy-MM-ddHH:mm:ss
并设置时区为东八区
示例代码
@JsonProperty("born_time") @JsonFormat(pattern="yyyy-MM-ddHH:mm:ss",timezone="GMT+8") privateDatebornTime;
结果
[{
"name":"小明",
"age":18,
"sex":"MALE",
"teacher":{
"name":"李老师",
"age":40
},
"father_name":"王富贵",
"born_time":"2020-09-1319:50:47"
},{
"name":"小花",
"age":16,
"sex":"FEMALE",
"teacher":{
"name":"赵老师",
"age":38
},
"father_name":"钱很多",
"born_time":"2020-09-1319:50:47"
}]
7.2sex设置为中文
只需要为Sex添加一个方法getOrdinal,并添加注解JsonValue即可
示例代码
@JsonValue publicStringgetOrdinal(){ returnname; }
示例结果
[{
"name":"小明",
"age":18,
"sex":"男",
"teacher":{
"name":"李老师",
"age":40
},
"father_name":"王富贵",
"born_time":"2020-09-1319:57:47"
},{
"name":"小花",
"age":16,
"sex":"女",
"teacher":{
"name":"赵老师",
"age":38
},
"father_name":"钱很多",
"born_time":"2020-09-1319:57:47"
}]
7.3sex设置为序号
有些场景喜欢用0和1等序号设置男女,即枚举的序号:0表示男,1表示女
此时需要修改Set的getOrdinal方法
- 修改返回值类型为int
- 调用父类的getOrdinal方法
示例代码
@JsonValue publicintgetOrdinal(){ returnsuper.ordinal(); }
示例结果
[{
"name":"小明",
"age":18,
"sex":0,
"teacher":{
"name":"李老师",
"age":40
},
"father_name":"王富贵",
"born_time":"2020-09-1320:01:44"
},{
"name":"小花",
"age":16,
"sex":1,
"teacher":{
"name":"赵老师",
"age":38
},
"father_name":"钱很多",
"born_time":"2020-09-1320:01:44"
}]
8拍平嵌套类型
场景
如前面提到的结果所示,teacher的两个属性并不在student的第一层,
有时可能会更深的层次,使用起来不太友好
如何用teacher_name和teacher_age两个属性代替teacher呢?
- 在Student的teacher属性上添加注解JsonUnwrapped,意为不包裹
- 在Teacher的属性上利用注解JsonProperty重命名
示例代码
Student.java
@JsonUnwrapped privateTeacherteacher;
Teacher.java
@JsonProperty("teacher_name") privateStringname; @JsonProperty("teacher_age") privateIntegerage;
示例结果
[{
"name":"小明",
"age":18,
"sex":0,
"teacher_name":"李老师",
"teacher_age":40,
"father_name":"王富贵",
"born_time":"2020-09-1320:21:53"
},{
"name":"小花",
"age":16,
"sex":1,
"teacher_name":"赵老师",
"teacher_age":38,
"father_name":"钱很多",
"born_time":"2020-09-1320:21:53"
}]
9自定义序列化器
9.1场景
假如需要将年龄调整为理论学龄,即将年龄减去7,得到理论学龄,如何操作呢?
创建自定义年龄序列化器AgeSerializer,继承StdSerializer<>
- 创建AgeSerializer的构造方法
- 重写serialize函数
利用注解修指定Student属性age的序列化器AgeSerializer
9.2示例代码
AgeSerializer.java
publicclassAgeSerializerextendsStdSerializer{ protectedAgeSerializer(){ super(Integer.class); } @Override publicvoidserialize(Integervalue,JsonGeneratorgen,SerializerProviderprovider)throwsIOException{ gen.writeNumber(value-7); } }
Student.java
@JsonSerialize(using=AgeSerializer.class) privateIntegerage;
9.3示例结果
[{
"name":"小明",
"age":11,
"sex":0,
"teacher_name":"李老师",
"teacher_age":40,
"father_name":"王富贵",
"born_time":"2020-09-1320:31:59"
},{
"name":"小花",
"age":9,
"sex":1,
"teacher_name":"赵老师",
"teacher_age":38,
"father_name":"钱很多",
"born_time":"2020-09-1320:31:59"
}]
10反序列化
10.1流程
首先需要有序列化好的数据,可以是string,byte[],文件二进制等
创建一个对象映射器,jackson包下的ObjectMapper
调用反序列化函数,本例中的readValue,将字符串转为对象
10.2反序列化对象数据
示例代码
publicvoidtestDeserializable()throwsJsonProcessingException{ Strings="{\"name\":\"小明\",\"age\":11,\"sex\":0,\"teacher_name\":\"李老师\",\"teacher_age\":40,\"father_name\":\"王富贵\",\"born_time\":\"2020-09-1320:46:10\"}"; ObjectMappermapper=newObjectMapper(); Studentstudent=mapper.readValue(s,Student.class); System.out.println(student); }
示例结果
Student{name='小明',age=11,sex=MALE,fatherName='王富贵',bornTime=SunSep1320:46:10CST2020,teacher=Teacher{name='李老师',age=40}}
分析
为了便于打印对象数据,重写了Student和Teacher的toString方法
从数据中可以看出,age的结果是错误的,原因在于之前自定义的序列化器将年龄减小了7,10.4节将会通过自定义反序列化器来解决此问题
10.3反序列化对象数组数据
示例代码
publicvoidtestDeserializableStudents()throwsJsonProcessingException{ Strings="[{\"name\":\"小明\",\"age\":11,\"sex\":0,\"teacher_name\":\"李老师\",\"teacher_age\":40,\"father_name\":\"王富贵\",\"born_time\":\"2020-09-1320:51:31\"},{\"name\":\"小花\",\"age\":9,\"sex\":1,\"teacher_name\":\"赵老师\",\"teacher_age\":38,\"father_name\":\"钱很多\",\"born_time\":\"2020-09-1320:51:31\"}]"; ObjectMappermapper=newObjectMapper(); Student[]students=mapper.readValue(s,Student[].class); for(Studentstudent:students){ System.out.println(student); } }
示例结果
Student{name='小明',age=11,sex=MALE,fatherName='王富贵',bornTime=SunSep1320:51:31CST2020,teacher=Teacher{name='李老师',age=40}}
Student{name='小花',age=9,sex=FEMALE,fatherName='钱很多',bornTime=SunSep1320:51:31CST2020,teacher=Teacher{name='赵老师',age=38}}
分析
readValue的第二个参数需要传类型,这里推荐用数组,不推荐用List,具体原因笔者目前也没花时间去研究
10.4自定义反序列化器
从10.2节及10.3的现象中可以看出来,仅仅自定义的序列化器会导致序列化的过程是正常的,反序列化的过程仍然是默认逻辑,有时候会导致意想不到的结果
遇到此场景,可以考虑自定义反序列化器
- 创建自定义反序列化器AgeDeserializer,继承StdDeserializer<>
- 重写deserialize方法
- 在Student的age属性上添加注解JsonDeserialize,并指定反序列化器AgeDeserializer
示例代码
AgeDeserializer.java
publicclassAgeDeserializerextendsJsonDeserializer{ @Override publicIntegerdeserialize(JsonParserp,DeserializationContextctxt)throwsIOException,JsonProcessingException{ returnp.getIntValue()+7; } }
Student.java
@JsonSerialize(using=AgeSerializer.class) @JsonDeserialize(using=AgeDeserializer.class) privateIntegerage;
示例结果
Student{name='小明',age=18,sex=MALE,fatherName='王富贵',bornTime=SunSep1320:51:31CST2020,teacher=Teacher{name='李老师',age=40}}
Student{name='小花',age=16,sex=FEMALE,fatherName='钱很多',bornTime=SunSep1320:51:31CST2020,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) publicclassStudent{
publicvoidtestNonDefault()throwsIOException{ Studentstudent=newStudent("",0,null,null,null,null); ObjectMappermapper=newObjectMapper(); Strings=mapper.writeValueAsString(student); System.out.println(s); }
示例输出
{
"name":"",
"age":-7
}
分析
当属性为默认值,即零值时,不序列化
常见的零值:
- int:0
- bool:false,
- String:null
12注解Transient
13注解JsonIgnore
总结
到此这篇关于jackson序列化和反序列化的应用实践指南的文章就介绍到这了,更多相关jackson序列化和反序列化内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。