跳至主要内容

[pkg] encoding/json

此篇為各筆記之整理,非原創內容,資料來源可見下方連結與文後參考資料:

TL;DR

  • json.Unmarshal 把 json 字串轉成 struct
  • json.Marshal 把 struct 轉成 json 字串

將 struct 轉成 JSON

定義可轉換成 JSON 的 struct

透過 struct field's tag 可以將 struct 轉換成特定的格式:

// struct fields tag: `json:"<field>,<options>"`
type Book struct {
BookID uint `json:"-"` // 轉換時總是忽略掉此欄位
Title string `json:"title"` // 轉換後 JSON 的欄位名稱是 title
Author string `json:"author"` // 轉換後 JSON 的欄位名稱是 author
Name string `json:"name,omitempty"` // 當 name 是空值時轉換後則無該欄位
Age uint `json:",omitempty"` // 當 Age 有值時,則 JSON 的欄位名稱是 "Age"(大寫開頭),否則不顯示該欄位
Price uint `json:"_,"` // 轉換後 JSON 的欄位名稱是 _
Configuration string `json:"configuration,string"`
}
  • -:field tag 如果是 - 則總是忽略掉該欄位,轉換後不會有該欄位
  • 在 options 可以有一寫特殊的選項:
    • omitempty :如果該 field 是空值,則忽略該 field 的欄位,空值包含 false, 0, nil pointer, nil interface value, 空陣列、空字串
    • string:如果某欄位的值又是 JSON 的話,可以在 options 中使用 string

使用 Marshal 方法

  • 把 struct 放入 json.Marshal(struct) 的方法中,會回傳 byte slice
  • 透過 string(byteSlice) 把 byte slice 轉成 string 即可得到 JSON
  • 在將 struct 轉換成 JSON 時會根據不同的型別轉換出不同的內容,多數的轉換都相當直覺能夠直接對應(例如 int 變成 number),比較特別的是
    • []byte 會轉換成 base64-encoded 的字串
    • nil 會轉換成 null
type Book struct {
BookID uint `json:"-"`
Title string `json:"title"`
Author string `json:"author"`
Name string `json:"Name,omitempty"`
Age uint `json:",omitempty"`
Price uint `json:"_,"`
}

func main() {
book := Book{BookID: 2, Title: "Learning Go", Author: "Gopher", Name: "", Age: 0, Price: 31900}

/* 將 struct 轉成 byte slice,再透過 string 變成 JSON 格式 */
byteSlice, _ := json.MarshalIndent(book, "", " ")
fmt.Println(string(byteSlice))

//{
// "title": "Learning Go",
// "author": "Gopher",
// "_": 31900
//}
}

將 struct 轉成 JSON(nested Object)

// Book struct 裡包含 Author struct
type Book struct {
Title string `json:"title"`
Author Author `json:"author"`
}

type Author struct {
Sales int `json:"book_sales"`
Age int `json:"age"`
Developer bool `json:"is_developer"`
}

func main() {
author := Author{
Sales: 3,
Age: 25,
Developer: true,
}
book := Book{Title: "Learning Go", Author: author}

/* 將 struct 轉成 byte slice,再透過 string 變成 JSON 格式 */
byteSlice, _ := json.MarshalIndent(book, "", " ")
fmt.Println(string(byteSlice))

// {
// "title": "Learning Go",
// "author": {
// "book_sales": 3,
// "age": 25,
// "is_developer": true
// }
// }
}

將 JSON 轉成 struct:結構化資料

  • 定義 JSON 檔案轉換後的 struct
  • 將 JSON 轉成 byte slice
  • 透過 json.Unmarshal(byte_slice, &struct) 將 byte slice 轉成 struct

⚠️ 如果要接收來自 JavaScript 時間格式,建議傳送 ISO 8601 的格式(而非時間戳記)會比較簡單,也就是 new Date().toISOString(),如果傳送的是 timestamp 可能會需要另外處理。


type SensorReading struct {
Name string `json:"name"`
Capacity int `json:"capacity"`
Time time.Time `json:"time"`
}

func main() {
jsonString := `{"name": "battery sensor", "capacity": 40, "time": "2019-01-21T19:07:28Z"}`

reading := SensorReading{}

/**
* 將 JSON 轉成 struct 需要先把 string 轉成 byte slice,
* 然後再透過 Unmarshal 把空的 Struct 帶入
**/
err := json.Unmarshal([]byte(jsonString), &reading)
if err != nil {
fmt.Println(err)
}

fmt.Printf("%+v\n", reading)
// {Name:battery sensor Capacity:40 Time:2019-01-21 19:07:28 +0000 UTC}
}

將 JSON 轉成 map:非結構化資料(unstructured data)

有些時候並沒有辦法事先知道 JSON 資料的格式會長什麼樣子,這時候可以使用 map[string]interface{} 這種寫法,如此將可以把 JSON 轉成 map:

type SensorReading struct {
Name string `json:"name"`
Capacity int `json:"capacity"`
Time string `json:"time"`
}

func main() {
jsonString := `{"name": "battery sensor", "capacity": 40, "time": "2019-01-21T19:07:28Z"}`

reading := make(map[string]interface{})

/* 將 JSON 轉成 struct 需要先把 string 轉成 byte slice,然後再透過 Unmarshal 把空的 Struct 帶入 */
err := json.Unmarshal([]byte(jsonString), &reading)
if err != nil {
fmt.Println(err)
}

fmt.Printf("%+v\n", reading)
// map[capacity:40 name:battery sensor time:2019-01-21T19:07:28Z]
}

將 JSON 轉成 struct:將 API 取得的 response 進行 JSON decode

keywords: json.NewDecoder, Decode
  • resp.Body 傳入 json.NewDecoder() 中,在使用 Decode 來將 JSON 轉成 Struct
// target is the struct of model
func getJson(url string, target interface{}) error {
// myClient is http.Client
resp, err := myClient.Get(url)
if err != nil {
return err
}

// remember to close tFhe Body
defer resp.Body.Close()

// create json Decoder and decode(可以縮寫成最下面一行)
decoder := json.NewDecoder(resp.Body)
err = decoder.Decode(target)
if err != nil {
return err
}

return nil

// json decode 的部分也可以縮寫成這一行
// return json.NewDecoder(r.Body).Decode(target)
}

Time:非正規時間格式解析

參考