jackson序列化與反序列化的應(yīng)用實(shí)踐
點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”
優(yōu)質(zhì)文章,第一時(shí)間送達(dá)
? 作者?|??zhouweixin?
來源 |? urlify.cn/iEbiAz
66套java從入門到精通實(shí)戰(zhàn)課程分享
1 相關(guān)概念
序列化: 把對(duì)象轉(zhuǎn)換為字節(jié)序列的過程稱為對(duì)象的序列化
反序列化: 把字節(jié)序列恢復(fù)為對(duì)象的過程稱為對(duì)象的反序列化
2 序列化的作用
用于把內(nèi)存中的對(duì)象狀態(tài)保存到一個(gè)文件中或者數(shù)據(jù)庫(kù)中
用于網(wǎng)絡(luò)傳送對(duì)象
用于遠(yuǎn)程調(diào)用傳輸對(duì)象
3 準(zhǔn)備序列化對(duì)象
準(zhǔn)備了兩個(gè)類, 教師類和學(xué)生類, 其中一個(gè)學(xué)生只有一個(gè)教師
這里省略了構(gòu)造方法和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中心倉(cāng)庫(kù)中選擇了2.11.2版本的jackson-databind
compile?group:?'com.fasterxml.jackson.core',?name:?'jackson-databind',?version:?'2.11.2'
5 序列化與格式化輸出
5.1 流程
首先需要有一個(gè)待序列化對(duì)象, 本例中的student對(duì)象
創(chuàng)建一個(gè)對(duì)象映射器, jackson包下的ObjectMapper
調(diào)用序列化函數(shù), 本例中的writeValueAsString, 將對(duì)象轉(zhuǎn)為字符串, 便于展示
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 結(jié)果
[?{
??"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 分析
示例中調(diào)用了方法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 場(chǎng)景
假如需要將序列化的json由駝峰命名修改為下劃線命名, 如fatherName修改為father_name
只需要在字段fatherName上用注解JsonProperty配置
6.2 示例代碼
@JsonProperty("father_name")
private?String?fatherName;
@JsonProperty("born_time")
private?Date?bornTime;
6.3 示例結(jié)果
[?{
??"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格式設(shè)置
當(dāng)前bornTime的格式為unix時(shí)間戮, 可讀性非常差
現(xiàn)修改為yyyy-MM-dd HH:mm:ss
并設(shè)置時(shí)區(qū)為東八區(qū)
示例代碼
@JsonProperty("born_time")
@JsonFormat(pattern?=?"yyyy-MM-dd?HH:mm:ss",?timezone?=?"GMT+8")
private?Date?bornTime;
結(jié)果
[?{
??"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設(shè)置為中文
只需要為Sex添加一個(gè)方法getOrdinal, 并添加注解JsonValue即可
示例代碼
@JsonValue
public?String?getOrdinal()?{
????return?name;
}
示例結(jié)果
[?{
??"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設(shè)置為序號(hào)
有些場(chǎng)景喜歡用0和1等序號(hào)設(shè)置男女, 即枚舉的序號(hào): 0表示男, 1表示女
此時(shí)需要修改Set的getOrdinal方法
修改返回值類型為int
調(diào)用父類的getOrdinal方法
示例代碼
@JsonValue
public?int?getOrdinal()?{
????return?super.ordinal();
}
示例結(jié)果
[?{
??"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 拍平嵌套類型
場(chǎng)景
如前面提到的結(jié)果所示, teacher的兩個(gè)屬性并不在student的第一層,
有時(shí)可能會(huì)更深的層次, 使用起來不太友好
如何用teacher_name和teacher_age兩個(gè)屬性代替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;
示例結(jié)果
[?{
??"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 場(chǎng)景
假如需要將年齡調(diào)整為理論學(xué)齡, 即將年齡減去7, 得到理論學(xué)齡, 如何操作呢?
創(chuàng)建自定義年齡序列化器AgeSerializer, 繼承StdSerializer<>
創(chuàng)建AgeSerializer的構(gòu)造方法
重寫serialize函數(shù)
利用注解修指定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 示例結(jié)果
[?{
??"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 流程
首先需要有序列化好的數(shù)據(jù), 可以是string, byte[], 文件二進(jìn)制等
創(chuàng)建一個(gè)對(duì)象映射器, jackson包下的ObjectMapper
調(diào)用反序列化函數(shù), 本例中的readValue, 將字符串轉(zhuǎn)為對(duì)象
10.2 反序列化對(duì)象數(shù)據(jù)
示例代碼
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);
}
示例結(jié)果
Student{name='小明',?age=11,?sex=MALE,?fatherName='王富貴',?bornTime=Sun?Sep?13?20:46:10?CST?2020,?teacher=Teacher{name='李老師',?age=40}}
分析
為了便于打印對(duì)象數(shù)據(jù), 重寫了Student和Teacher的toString方法
從數(shù)據(jù)中可以看出, age的結(jié)果是錯(cuò)誤的, 原因在于之前自定義的序列化器將年齡減小了7, 10.4節(jié)將會(huì)通過自定義反序列化器來解決此問題
10.3 反序列化對(duì)象數(shù)組數(shù)據(jù)
示例代碼
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);
????}
}
示例結(jié)果
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的第二個(gè)參數(shù)需要傳類型, 這里推薦用數(shù)組, 不推薦用List, 具體原因筆者目前也沒花時(shí)間去研究
10.4 自定義反序列化器
從10.2節(jié)及10.3的現(xiàn)象中可以看出來, 僅僅自定義的序列化器會(huì)導(dǎo)致序列化的過程是正常的, 反序列化的過程仍然是默認(rèn)邏輯, 有時(shí)候會(huì)導(dǎo)致意想不到的結(jié)果
遇到此場(chǎng)景, 可以考慮自定義反序列化器
創(chuàng)建自定義反序列化器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;
示例結(jié)果
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
該注解使用在實(shí)體類上, 格式@JsonInclude(value = JsonInclude.Include.NON_DEFAULT)
其中, Include有7種參數(shù), 功能對(duì)比如下
| 參數(shù) | 功能 | 備注 |
|---|---|---|
| Include.ALWAYS | 屬性總是序列化(需要有g(shù)et方法) | 默認(rèn)值 |
| Include.NON_DEFAULT | 屬性為默認(rèn)值不序列化 | 如: 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
}
分析
當(dāng)屬性為默認(rèn)值, 即零值時(shí), 不序列化
常見的零值:
int: 0
bool: false,
String: null
12 注解JsonIgnoreProperties
該注解為類注解, 配置忽略序列化和反序列化的字段名
如下所示, 忽略字段name和age
@JsonIgnoreProperties(value?=?{"name",?"age"})
@JsonInclude(value?=?JsonInclude.Include.NON_DEFAULT)
public?class?Student?{
13 注解JsonIgnore
該注解為屬性注解, 表示忽略當(dāng)前屬性, 如下所示, 表示忽略name字段
@JsonIgnore
private?String?name;
粉絲福利:108本java從入門到大神精選電子書領(lǐng)取
???
?長(zhǎng)按上方鋒哥微信二維碼?2 秒 備注「1234」即可獲取資料以及 可以進(jìn)入java1234官方微信群
感謝點(diǎn)贊支持下哈?
