如何控制 Go 編碼 JSON 資料時的行為
今天來聊一下我在 Go 中對資料進行 JSON 編碼時遇到次數最多的三個問題以及解決方法,大家來看看是不是也為這些問題撓掉了不少頭髮。
自定義 JSON 鍵名
這個問題加到文章裡我是有所猶豫的,因為基本上大家都會,不過屬於同類問題我還是放進來了,對新接觸 Go 的同學更友好些。
我們先從最常見的一個問題說,首先在 Go 程式中要將資料編碼成 JSON 格式時通常我們會先定義結構體型別,將資料存放到結構體變數中。
type Address struct {
Type string
City string
Country string
}
type CreditCard struct {
FirstName string
LastName string
Addresses []*Address
Remark string
}
home := &Address{"private", "Aartselaar", "Belgium"}
office := &Address{"work", "Boom", "Belgium"}
card := VCard{"Jan", "Kersschot", []*Address{home, office}, "none"}
js, err := json.Marshal(card)
fmt.Printf("JSON format: %s", js)
只有匯出的結構體成員才會被編碼,這也就是我們為什麼選擇用大寫字母開頭的欄位名稱。在編碼時,預設使用結構體欄位的名字作為 JSON 物件中的 key,但是一般 JSON 是給 HTTP 介面返回資料使用的,在介面的規範裡針對資料我們一般都要求返回 snake case 風格的欄位名。解決這個問題的方法是在結構體宣告時在結構體欄位標籤裡可以自定義對應的 JSON key
所以我們把結構體宣告改為如下即可:
type Address struct {
Type string `json:"type"`
City string `json:"city"`
Country string `json:"country"`
}
編碼 JSON 時忽略掉指定欄位
並不是所有資料我們都期望編碼到 JSON 中暴露給外部介面的,所以針對一些敏感的欄位我們往往希望將其從編碼後的 JSON 資料中忽略掉。那麼上面也說了只有匯出的結構體成員才會被編碼,有的同學會問我直接用小寫的欄位名不行嗎?可是未匯出欄位只能在包內訪問,像這種攜帶內部敏感資料的往往都是應用的基礎資料,由專案的公共包來提供的。那麼怎麼既能維持欄位的匯出性又能讓其在 JSON 資料中被忽略掉呢?還是使用結構體的標籤進行註解,比如下面定義的結構體,可以把身份證 IdCard 欄位在 JSON 資料中去掉:
type User struct {
Name string `json:"name"`
Age Int `json:"int"`
IdCard string `json:"-"`
}
encoding/json 的原始碼中和文件中都列舉了通過結構體欄位標籤控制資料 JSON 編碼行為的說明:
// Field is ignored by this package.
Field int `json:"-"`
// Field appears in JSON as key "myName".
Field int `json:"myName"`
// Field appears in JSON as key "myName" and
// the field is omitted from the object if its value is empty,
// as defined above.
Field int `json:"myName,omitempty"`
// Field appears in JSON as key "Field" (the default), but
// the field is skipped if empty.
// Note the leading comma.
Field int `json:",omitempty"`
omitempty 這個是欄位的資料為空時,在 JSON 中省略這個欄位。為的是節省資料空間,Protobuf 編譯器生成的結構體程式碼中每個欄位標籤中都有 omitempty。但是在 Api 開發中這個不常用,因為欄位不固定對前端很不友好。
對 Protobuf 不瞭解的可以看我之前寫的文章《Protobuf 語言指南》。
結構體欄位標籤的 json 註解中都不加 omitempty 後還遇到一種情況,就是資料型別為切片的欄位在資料為空的時候會被 JSON 編碼為 null 而不是 []。這個前端經常會問我沒資料的時候能不能不要返回 null,每回還要多寫一個判斷。我的說辭都是不能,其實規範點講是應該返回 [] 的知識我是我自己沒找到到解決方法。作為一個在寫程式碼上有強迫症的人,這個問題還是想搞明白的,好在有一天在 StackOverflow 上看到一個答案,才發現是編碼的疏忽導致的。
解決空切片在 JSON 裡被編碼成 null
因為切片的零值為 nil,無指向記憶體的地址,所以當以這種形式定義 var f [] int 初始化 slice 後,在 JSON 中將其編碼為 null,如果想在 JSON 中將空 slice 編碼為 [] 則需用 make 初始化 slice 為其分配記憶體地址:
執行下面的例子可以看出兩點的區別:
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Friends []string
}
func main() {
var f1 []string
f2 := make([]string, 0)
json1, _ := json.Marshal(Person{f1})
json2, _ := json.Marshal(Person{f2})
fmt.Printf("%s\n", json1)
fmt.Printf("%s\n", json2)
}
輸出:
{"Friends":null}
{"Friends":[]}
其實導致這個問題的原因是 Go 的 append 函式(甩鍋),我們都知道引用型別的變數定義後如果沒初始化他們的值是 nil,無指向記憶體的地址,是無法直接使用的。但是 append 函式在給切片追加元素時會判斷切片是否已初始化,沒有的話會幫其初始化分配底層陣列。我的習慣是先宣告切片,然後再在下面的迴圈程式碼中向切片追加元素。但是如果迴圈沒有執行,比如你從資料庫沒查出資料,就會導致對應切片欄位在無資料時返回的是 nil 然後被 JSON 編碼成了 null。所以這個算是一個經驗總結出來的 Tip 吧在寫程式碼時大家一定要注意了。
這就是我在開發時把資料編碼成 JSON 格式時遇到的三個問題和相應的解決方法。加上之前寫的解析 JSON 的文章,兩個文章加起來差不多就能彙總日常開發中關於 encoding/json 庫使用的各種問題了。
原文地址:https://zhuanlan.zhihu.com/p/104757141
- 加微信實戰群請加微信(註明:實戰群):gocnio
相關文章
- Go JSON編碼與解碼?GoJSON
- Go - 如何解析 JSON 資料?GoJSON
- Go之json資料GoJSON
- 如何為機器學習進行資料標籤、版本控制和管理機器學習
- 如何對DevOps資料庫進行原始碼控制dev資料庫原始碼
- JSON編碼格式提交表單資料詳解JSON
- Go 裡的超時控制Go
- Go語言轉換JSON資料GoJSON
- Map 轉json資料,json資料轉換為MapJSON
- lazarus資料序列為JSONJSON
- JSON資料如何進行解析呢,方式有哪些?JSON
- 將物件解析為JSON資料和將JSON資料解析為物件的簡單例項物件JSON單例
- Go語言基礎-常見編碼(Json、Base64)GoJSON
- 如何為資料行的新增操作按鈕
- SAP UI5 Table 控制元件資料進行 Excel 匯出時如何進行格式控制試讀版UI控制元件Excel
- 一文搞懂如何實現 Go 超時控制Go
- 利用Jquery的map函式將json資料行轉化為表格jQuery函式JSON
- 程式碼控制 textarea 控制元件是否為KindEditor 編輯框控制元件
- go裡面如何將[]int json序列化為[]string?GoJSON
- 為程式碼編寫穩定的單元測試 [Go]Go
- Go抓取網頁資料並存入MySQL和返回json資料Go網頁MySqlJSON
- mysql 建立 資料庫時指定編碼很重要MySql資料庫
- (iphone/ipad)解析json時指定字元編碼問題iPhoneiPadJSON字元
- MySql修改資料庫編碼為UTF8MySql資料庫
- [譯]使用Go Cloud的Wire進行編譯時依賴注入GoCloud編譯依賴注入
- 如何取出 API 呼叫返回的 JSON 資料APIJSON
- 將json資料轉換為Python字典將json資料轉換為Python字典JSONPython
- 為資料集而生的 SQL 控制檯SQL
- 匯入大容量資料時控制觸發器執行觸發器
- java連線資料庫時jdbc設定編碼Java資料庫JDBC
- 輸出 JSON 資料時的 Content-TypeJSON
- 為啥我的字典資料轉換不成 json?JSON
- 分割模型的資料集由json轉為txt模型JSON
- AssetHook:Android應用資源資料執行時編輯工具HookAndroid
- 基於伺服器響應的實時天氣資料進行JSON解析的詳細程式碼及其框架伺服器JSON框架
- 資料分析:如何將原始資料轉化為有價值的行動?
- ORACLE 修改資料庫的字符集編碼為UTF-8Oracle資料庫
- Android如何回撥編碼後的音視訊資料Android