問題引入
當某個struct存在某個欄位為string或者[]byte類型但是實際上儲存的內容是json格式的資料時,對其進行json序列化,比如
type Message struct { From string `json:"from"` To string `json:"to"` Data string `json:"data"`}func main() { msg := Message{ From: "XiaoMing", To: "LiGang", Data: `{"title":"test","body":"something"}`, } jsonData, err := json.Marshal(msg) if err != nil { panic(err) } fmt.Println(string(jsonData))}
在上面的例子中,Data欄位是string類型,但是儲存的內容是json格式的資料,這個時候,程式輸出:
{"from":"XiaoMing","to":"LiGang","data":"{\"title\":\"test\",\"body\":\"something\"}"}
可以看到,序列化之後的data是一個字串。
如果Message對應的是資料庫中的一張表,而data欄位在資料庫中是json類型,當我們需要一個介面,查詢Message表中的記錄返回給用戶端。如果直接執行序列化,那麼用戶端擷取到的Data實際上是一個字串,用戶端還需要自行對這個字串進行json還原序列化。
這時候我們就會想,有沒有什麼辦法能夠在服務端序列化Message時,將data欄位序列化成json對象而不是字串呢?
自訂序列化
因為data欄位的值本身就是json類型,為什麼不能在序列化時直接使用呢?
查看json包的官方文檔,我們可以發現關於 自訂序列化的例子
當執行json序列化時,如果對應的類型實現了Marshaler
介面:
type Marshaler interface { MarshalJSON() ([]byte, error)}
那麼就會執行其MarshalJSON
方法,並將返回的位元組數組作為該值的序列化值。
那麼回到上面的例子,我們就很容易實現目標:
type JsonString stringfunc (j JsonString) MarshalJSON() ([]byte, error) { fmt.Println("marshal...") return []byte(j), nil}type Message struct { From string `json:"from"` To string `json:"to"` Data JsonString `json:"data"`}
在上面的代碼中基於string
型別宣告了JsonString
,代表json格式的字串,並實現了Marshaler介面。因為JsonString代表的就是json字串,直接將其轉換成位元組數組返回。
然後將Message中的Data欄位換成JsonString類型。
再次執行程式,可以看到:
{"from":"XiaoMing","to":"LiGang","data":{"title":"test","body":"something"}}
Perfect!