Golang?中的json.Marshal問題總結(jié)(推薦)
1.Quiz
有如下一個例子:
package main
import (
"encoding/json"
"fmt"
"time"
)
type RecordBrief struct {
time.Time
ID int
}
func main() {
r := RecordBrief{
Time: time.Now(),
ID: 6,
}
m, _ := json.MarshalIndent(r, "", "\t")
fmt.Println(string(m))
}
你期望的結(jié)果是像:
{
"Time": "2022-06-25T10:49:39.597537249+08:00",
"ID": 6
}
還是:
{
"ID": 6
}
或者是別的?
2.Answer
其實(shí)如果你認(rèn)為的答案不是:
"2022-06-25T10:52:23.590933959+08:00"
也沒能想明白原因,可以繼續(xù)往下看看。
3.Resolving
誠然,我們在學(xué)習(xí)json的序列化和反序列化的時候,目的就是把一個Golang struct值序列化為對應(yīng)的json string罷了??赡芪覀冞€知道一些Marshal的規(guī)則,比如struct的字段需要定義為可導(dǎo)出的,比如還可通過定義對應(yīng)的json tag來修改struct field對應(yīng)的json字段名稱等。
但是對于json.Marshal函數(shù)的細(xì)節(jié)可能大家不會去太在意。本次提出的問題中,我們不難注意到其中的time.Time是一個匿名(Anonymous)字段,而這個就是答案的由來。我們先看看json.Marshal的注釋文檔中的一個解釋:
// ...
// Marshal traverses the value v recursively.
// If an encountered value implements the Marshaler interface
// and is not a nil pointer, Marshal calls its MarshalJSON method
// to produce JSON. If no MarshalJSON method is present but the
// value implements encoding.TextMarshaler instead, Marshal calls
// its MarshalText method and encodes the result as a JSON string.
// ...
func Marshal(v interface{}) ([]byte, error) {
...
}
Marshal函數(shù)遞歸地遍歷傳入的序列化對象v(及其成員)。當(dāng)面對一個實(shí)現(xiàn)了json.Marshaler接口的對象(不能是一個空指針)時,Marshal函數(shù)就會調(diào)用該對象的MarshalJSON方法來生成JSON內(nèi)容。如果沒有實(shí)現(xiàn)json.Marshaler,而是實(shí)現(xiàn)了encoding.TextMarshaler接口,那么就會調(diào)用它的MarshalText方法,然后把該方法返回的結(jié)果轉(zhuǎn)編為一個JSON字符串。
然后我們再看看time.Time:
type Time struct {
...
}
// MarshalJSON implements the json.Marshaler interface.
// The time is a quoted string in RFC 3339 format, with sub-second precision added if present.
func (t Time) MarshalJSON() ([]byte, error) {
if y := t.Year(); y < 0 || y >= 10000 {
// RFC 3339 is clear that years are 4 digits exactly.
// See golang.org/issue/4556#c15 for more discussion.
return nil, errors.New("Time.MarshalJSON: year outside of range [0,9999]")
}
b := make([]byte, 0, len(RFC3339Nano)+2)
b = append(b, '"')
b = t.AppendFormat(b, RFC3339Nano)
b = append(b, '"')
return b, nil
}
所以time.Time是實(shí)現(xiàn)了json.Marshaler接口的。然后觀察到它的實(shí)現(xiàn)是把時間按照RFC3339Nano格式字符串值返回為json序列化結(jié)果,這和我們實(shí)際上運(yùn)行程序看到的結(jié)果是一致的。
那么再看看我們的type定義:
type RecordBrief struct {
time.Time
ID int
}
為什么ID字段不見了?正是因為匿名字段的原因,Golang中的這種用法有點(diǎn)類似于繼承,所以RecordBrief類型也自動具有了time.Time的所有方法,當(dāng)然也包括了MarshalJSON,從而也就實(shí)現(xiàn)了json.Marshaler接口。如此一來,當(dāng)一個RecordBrief被Marshal的時候,它的序列化結(jié)果就被time.Time的序列化結(jié)果給覆蓋了。
Conclusion
如果你和我一樣,沒能一下知道原因,那多半是對一些常見的知識了解的深度和廣度不夠。我之前確實(shí)不知道time.Time居然也實(shí)現(xiàn)了json.Marshaler接口,也不清楚json.Marshal到底在做什么,所以不知道答案也就理所當(dāng)然了,后來經(jīng)過閱讀文檔注釋,才終于對該問題有了一些認(rèn)知(后續(xù)應(yīng)該總結(jié)一篇json.Marshal的源碼解析)。
至此,如果我們想要這種樣子的結(jié)果:
{
"Time": "2022-06-25T10:49:39.597537249+08:00",
"ID": 6
}
最簡單的方式是修改struct,將time.Time作為一個非匿名的導(dǎo)出字段:
type RecordBrief struct {
Time time.Time
ID int
}
另一種方法是給我們的RecordBrief實(shí)現(xiàn)json.Marshaler接口:
type RecordBrief struct {
time.Time
ID int
}
func (r RecordBrief) MarshalJSON() ([]byte, error) {
//非常簡單的一種方式就是創(chuàng)建中間類型
t := struct {
Time time.Time
ID int
}{
r.Time,
r.ID,
}
return json.Marshal(t)
}到此這篇關(guān)于Golang 中的json.Marshal問題總結(jié)的文章就介紹到這了,更多相關(guān)Golang json.Marshal內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
golang中拿slice當(dāng)queue和拿list當(dāng)queue使用分析
這篇文章主要為大家介紹了golang?中拿slice當(dāng)queue和拿list當(dāng)queue使用分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08
GO語言gin框架實(shí)現(xiàn)管理員認(rèn)證登陸接口
這篇文章主要介紹了GO語言gin框架實(shí)現(xiàn)管理員認(rèn)證登陸接口,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-10-10

