Go 常用包: 結構體匿名字段的 Json 序列化、反序列化

原創 劉慶輝 猿碼記

1. 介紹

Go的標準包 encoding/json實現了json對象的編碼和解碼。

這篇文章主要講的是結構體中嵌套匿名字段時的序列化和反序列化,因結構體在參與序列時會有很多細節規則,平時常用的也是結構體,至於map和slice可參考歷史文章 Go 學習 (二十三):JSON 編碼解析使用

1.1 Go 和 JSON 數據類型

sWcfqH

1.2 結構體序列化規則

@注意:可導出的字段 (首字母大寫),才能參與 Json 的序列化 ezXzVU a. 代碼示例

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. 匿名字段有標籤

4.2 反序列化

a. 匿名字段無標籤
b. 匿名字段有標籤

當結構體中嵌套匿名結構體字段時,在進行序列化和反序列時,推薦爲匿名字段加上 json 標籤。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/jrwu5xmAODF6s8Ab3vCHrg