Go 常用包: 結構體匿名字段的 Json 序列化、反序列化
原創 劉慶輝 猿碼記
1. 介紹
Go
的標準包encoding/json
實現了json對象
的編碼和解碼。
這篇文章主要講的是結構體中嵌套匿名字段時的序列化和反序列化,因結構體在參與序列時會有很多細節規則,平時常用的也是結構體,至於map和slice
可參考歷史文章 Go 學習 (二十三):JSON 編碼解析使用
1.1 Go 和 JSON 數據類型
1.2 結構體序列化規則
@注意:可導出的字段 (首字母大寫),才能參與 Json 的序列化
type Student struct {
// 指定json標籤時,序列化的key爲標籤值:name
Name string `json:"name"`
// 不指定序列化標籤時,key爲原屬性:Age
Age int
// 當標籤值爲`json:"-"`,代表改字段會被忽略
Home string `json:"-"`
// 標籤指定omitempty選項,代表該字段爲空值時,會被忽略
Phone string `json:"phone,omitempty"`
// 標籤指定string選項,代表輸出類型會轉化爲字符串
// 它只適用於字符串、浮點數、整數類型的字段
Score float64 `json:"score,string"`
}
b. 運行效果
func TestMarshal(t *testing.T) {
// 聲明初始化結構體
student1 := Student{
Name: "張三",
Age: 18,
Home: "北京",
Score: 90.5,
Phone: "",
}
// 序列化
json1, _ := json.Marshal(student1)
fmt.Printf("序列化json:%s\n", json1)
}
/**輸出
=== RUN TestMarshal
序列化json: {"name":"張三","Age":18,"score":"90.5"}
--- PASS: TestMarshal (0.00s)
PASS
*/
2. 匿名字段序列化
2.1 無JSON
標籤
a. 字段標籤不重複
School.Name 和 StudentA.Name,Json 標籤不一致。
// 學校
type School struct {
Name string `json:"schoolName"`
Address string `json:"schoolAddress"`
}
// 學生
type StudentA struct {
Name string `json:"name"`
// 匿名字段,而且沒有json標籤
School
}
// 序列化-匿名字段 (默認字段不衝突)
func TestAnonymousTagDifferent(t *testing.T) {
var student = StudentA{
Name: "小明",
School:School{
Name: "北京大學",
Address: "北京海淀區",
},
}
jsonByte, _ := json.Marshal(student)
fmt.Printf("json: %s \n",jsonByte)
}
/** 輸出
=== RUN TestAnonymousTagDifferent
json: {"name":"小明","schoolName":"北京大學","schoolAddress":"北京海淀區"}
--- PASS: TestAnonymousTagDifferent (0.00s)
PASS
*/
b. 字段標籤重複
Class.Name 和 StudentB.Name,Json 標籤一致,都是 json:"name"
。
// 學生
type StudentB struct {
// 標籤名和班級名一樣
Name string `json:"name"`
// 匿名字段,而且沒有json標籤
Class
}
// 班級
type Class struct {
// 標籤名和學生名一樣
Name string `json:"name"`
Desc string `json:"desc"`
}
// 序列化-匿名字段 (字段標籤衝突)
func TestAnonymousTagSame(t *testing.T) {
var student = StudentB{
Name: "小明",
Class: Class{
Name: "高二(1)班",
Desc: "優秀班級",
},
}
jsonByte, _ := json.Marshal(student)
fmt.Printf("json: %s \n", jsonByte)
}
/** 輸出
=== RUN TestAnonymousTagSame
json: {"name":"小明","desc":"優秀班級"}
--- PASS: TestAnonymousTagSame (0.00s)
PASS
*/
根據上面代碼,得知如果字段標籤衝突,衝突的匿名字段會被忽略。
2.2 有JSON
標籤
當匿名字段設置json
標籤時, 就不會出現衝突的情況,因爲序列化後的匿名字段會變成對象。
a. 字段標籤重複
type Bird struct {
// json標籤一樣
Name string `json:"name"`
// 匿名字段,有json標籤
Category `json:"category"`
}
type Category struct {
// json標籤一樣
Name string `json:"name"`
}
// 序列化-匿名字段,有json標籤
func TestAnonymousWithTag(t *testing.T) {
b := Bird{
Name: "喜鵲",
Category:Category{Name: "鳥類"},
}
jsonByte, _ := json.Marshal(b)
fmt.Printf("json: %s \n", jsonByte)
}
/** 輸出
=== RUN TestAnonymousWithTag
json: {"name":"喜鵲","category":{"name":"鳥類"}}
--- PASS: TestAnonymousWithTag (0.00s)
PASS
*/
b. 字段標籤不重複
type Bird struct {
Name string `json:"name"`
// 匿名字段,有json標籤
Category `json:"category"`
}
type Category struct {
Name string `json:"categoryName"`
}
// 序列化-匿名字段,有json標籤
func TestAnonymousWithTag(t *testing.T) {
b := Bird{
Name: "喜鵲",
Category:Category{Name: "鳥類"},
}
jsonByte, _ := json.Marshal(b)
fmt.Printf("json: %s \n", jsonByte)
}
/** 輸出
=== RUN TestAnonymousWithTag
json: {"name":"喜鵲","category":{"categoryName":"鳥類"}}
--- PASS: TestAnonymousWithTag (0.00s)
PASS
*/
3. 匿名字段反序列化
3.1 無JSON
標籤
a. 字段標籤不重複
type BookClass struct {
Name string `json:"className"`
Company string `json:"company"`
}
type GoStudy struct {
Name string `json:"name"`
Desc string `json:"desc"`
// 匿名字段無 json標籤
BookClass
}
func TestUnMarshal(t *testing.T) {
jsonStr := `{"name":"Go語言高級編程","desc":"這是一本Go學習書籍","className":"IT行業書籍","company":"xxx出版社"}`
var goStudy GoStudy
err := json.Unmarshal([]byte(jsonStr), &goStudy)
if err != nil {
t.Error(err)
return
}
fmt.Printf("反序列化結果: %+v\n",goStudy)
}
/** 輸出
=== RUN TestUnMarshal
反序列化結果: {Name:Go語言高級編程 Desc:這是一本Go學習書籍 BookClass:{Name:IT行業書籍 Company:xxx出版社}}
--- PASS: TestUnMarshal (0.00s)
PASS
*/
b. 字段標籤重複
type BookClass struct {
// 設置成標籤都爲 name
Name string `json:"name"`
Company string `json:"company"`
}
type GoStudy struct {
//設置成標籤都爲 name
Name string `json:"name"`
Desc string `json:"desc"`
// 匿名字段無 json標籤
BookClass
}
func TestUnMarshal(t *testing.T) {
jsonStr := `{"desc":"這是一本Go學習書籍","company":"xxx出版社","name":"IT行業書籍"}`
var goStudy GoStudy
err := json.Unmarshal([]byte(jsonStr), &goStudy)
if err != nil {
t.Error(err)
return
}
fmt.Printf("反序列化結果: %+v\n",goStudy)
}
/** 輸出
=== RUN TestUnMarshal
反序列化結果: {Name:IT行業書籍 Desc:這是一本Go學習書籍 BookClass:{Name: Company:xxx出版社}}
--- PASS: TestUnMarshal (0.00s)
PASS
*/
@注意: 當字段標籤重複時, 反序列化會優先給主屬性字段賦值。
3.2 有JSON
標籤
a. 字段標籤重複
type BookClass struct {
Name string `json:"name"`
Company string `json:"company"`
}
type GoStudy struct {
Name string `json:"name"`
Desc string `json:"desc"`
// 設置成有json標籤
BookClass `json:"bookClass"`
}
func TestUnMarshal(t *testing.T) {
jsonStr1 := `{"name":"Go語言高級編程","desc":"這是一本Go學習書籍","company":"xxx出版社"}`
jsonStr2 := `{"name":"Go語言高級編程","desc":"這是一本Go學習書籍","bookClass":{"name":"IT行業書籍","company":"xxx出版社"}}`
var res1 GoStudy
var res2 GoStudy
_ = json.Unmarshal([]byte(jsonStr1), &res1)
_ = json.Unmarshal([]byte(jsonStr2), &res2)
fmt.Printf("res1結果: %+v\n", res1)
fmt.Printf("res2結果: %+v\n", res2)
}
/** 輸出
=== RUN TestUnMarshal
res1結果: {Name:Go語言高級編程 Desc:這是一本Go學習書籍 BookClass:{Name: Company:}}
res2結果: {Name:Go語言高級編程 Desc:這是一本Go學習書籍 BookClass:{Name:IT行業書籍 Company:xxx出版社}}
--- PASS: TestUnMarshal (0.00s)
PASS
*/
b. 字段標籤不重複
type BookClass struct {
Name string `json:"bookName"`
Company string `json:"company"`
}
type GoStudy struct {
Name string `json:"name"`
Desc string `json:"desc"`
// 設置成有json標籤
BookClass `json:"bookClass"`
}
func TestUnMarshal(t *testing.T) {
jsonStr1 := `{"bookName":"Go語言高級編程","desc":"這是一本Go學習書籍","company":"xxx出版社"}`
jsonStr2 := `{"name":"Go語言高級編程","desc":"這是一本Go學習書籍","bookClass":{"bookName":"IT行業書籍","company":"xxx出版社"}}`
var res1 GoStudy
var res2 GoStudy
_ = json.Unmarshal([]byte(jsonStr1), &res1)
_ = json.Unmarshal([]byte(jsonStr2), &res2)
fmt.Printf("res1結果: %+v\n", res1)
fmt.Printf("res2結果: %+v\n", res2)
}
/** 輸出
=== RUN TestUnMarshal
res1結果: {Name: Desc:這是一本Go學習書籍 BookClass:{Name: Company:}}
res2結果: {Name:Go語言高級編程 Desc:這是一本Go學習書籍 BookClass:{Name:IT行業書籍 Company:xxx出版社}}
--- PASS: TestUnMarshal (0.00s)
PASS
*/
4. 匿名字段json
總結
4.1 序列化
a. 匿名字段無標籤
-
當匿名字段沒有指定標籤時,序列化後的結構爲同級, 如
{"..":"..",..}
-
當匿名屬性和主屬性的字段標籤一樣時,序列化會忽略匿名屬性的字段。
-
當匿名屬性和主屬性的字段標籤不一樣時,序列化不忽略任何字段。
b. 匿名字段有標籤
-
當匿名字段
a
指定標籤時,序列化後的結構爲上下級, 如{"..":"..","a":{"xx":"xx"}}
-
匿名屬性和主屬性字段,標籤無論是否一致,序列化都不會忽略任何字段。
4.2 反序列化
a. 匿名字段無標籤
-
當匿名字段沒有指定標籤時,可解析的
JSON
結構, 爲:{"..":"..",..}
-
當匿名屬性和主屬性的字段標籤一樣時,會優先將值賦給主屬性,匿名屬性爲類型零值。
-
當匿名屬性和主屬性的字段標籤不一樣時,會正常解析。
b. 匿名字段有標籤
-
當匿名字段指定標籤時,可解析的
JSON
結構, 爲:{"..":"..","a":{"xx":"xx"}}
-
匿名屬性和主屬性字段,標籤無論是否一致,反序列化都能正常賦值。
當結構體中嵌套匿名結構體字段時,在進行序列化和反序列時,推薦爲匿名字段加上 json 標籤。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/jrwu5xmAODF6s8Ab3vCHrg