源碼地址:https://github.com/zhouweixin/serializable
1 相關概念
序列化: 把對象轉換為字節序列的過程稱為對象的序列化
反序列化: 把字節序列恢復為對象的過程稱為對象的反序列化
2 序列化的作用
用于把內存中的對象狀態保存到一個文件中或者數據庫中
用于網絡傳送對象
用于遠程調用傳輸對象
3 準備序列化對象
準備了兩個類, 教師類和學生類, 其中一個學生只有一個教師
這里省略了構造方法和setter, getter方法
Teacher.java
1
2
3
4
|
public class Teacher { private String name; private Integer age; } |
Student.java
1
2
3
4
5
6
7
8
9
10
|
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
1
2
3
4
5
6
7
8
9
10
11
12
13
|
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
1
|
compile group: 'com.fasterxml.jackson.core' , name: 'jackson-databind' , version: '2.11.2' |
5 序列化與格式化輸出
5.1 流程
首先需要有一個待序列化對象, 本例中的student對象
創建一個對象映射器, jackson包下的ObjectMapper
調用序列化函數, 本例中的writeValueAsString, 將對象轉為字符串, 便于展示
5.2 代碼
1
2
3
4
5
6
7
8
9
10
11
|
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<Student> 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 結果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
[ { "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的格式
否則將打印
1
|
[{ "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 示例代碼
1
2
3
4
|
@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
并設置時區為東八區
示例代碼
1
2
3
|
@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即可
示例代碼
1
2
3
4
|
@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方法
示例代碼
1
2
3
4
|
@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
1
2
|
@JsonUnwrapped private Teacher teacher; |
Teacher.java
1
2
3
4
|
@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
1
2
3
4
5
6
7
8
9
10
|
public class AgeSerializer extends StdSerializer<Integer> { protected AgeSerializer() { super (Integer. class ); } @Override public void serialize(Integer value, JsonGenerator gen, SerializerProvider provider) throws IOException { gen.writeNumber(value - 7 ); } } |
Student.java
1
2
|
@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 反序列化對象數據
示例代碼
1
2
3
4
5
6
|
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 反序列化對象數組數據
示例代碼
1
2
3
4
5
6
7
8
|
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
1
2
3
4
5
6
|
public class AgeDeserializer extends JsonDeserializer<Integer> { @Override public Integer deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { return p.getIntValue() + 7 ; } } |
Student.java
1
2
3
|
@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
1
2
|
@JsonInclude (value = JsonInclude.Include.NON_DEFAULT) public class Student { |
1
2
3
4
5
6
|
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
}
分析
當屬性為默認值, 即零值時, 不序列化
常見的零值:
- int: 0
- bool: false,
- String: null
12 注解Transient
13 注解JsonIgnore
總結
到此這篇關于jackson序列化和反序列化的應用實踐指南的文章就介紹到這了,更多相關jackson序列化和反序列化內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!
原文鏈接:https://www.cnblogs.com/zhouweixin/p/13663422.html