2 回答

TA貢獻(xiàn)1946條經(jīng)驗(yàn) 獲得超4個(gè)贊
這是預(yù)期的結(jié)果,記錄在json.Marshal():
映射值編碼為 JSON 對(duì)象。映射的鍵類型必須是字符串、整數(shù)類型或?qū)崿F(xiàn) encoding.TextMarshaler。通過應(yīng)用以下規(guī)則對(duì)映射鍵進(jìn)行排序并將其用作 JSON 對(duì)象鍵,并遵守針對(duì)上述字符串值描述的 UTF-8 強(qiáng)制轉(zhuǎn)換:
- string keys are used directly
- encoding.TextMarshalers are marshaled
- integer keys are converted to strings
請(qǐng)注意,映射鍵的處理方式與屬性值的處理方式不同,因?yàn)?JSON 中的映射鍵是始終為值的屬性名稱string(而屬性值可能是 JSON 文本、數(shù)字和布爾值)。
根據(jù)文檔,如果您希望它也適用于地圖鍵,請(qǐng)實(shí)施encoding.TextMarshaler:
func (a Int) MarshalText() (text []byte, err error) {
test := a / 10
return []byte(fmt.Sprintf("%d-%d", a, test)), nil
}
(請(qǐng)注意,它MarshalText()應(yīng)該返回“只是”簡單文本,而不是 JSON 文本,因此我們?cè)谄渲惺÷粤?JSON 封送處理?。?/p>
這樣,輸出將是(在Go Playground上嘗試):
array ["100-10","200-20"] <nil>
map wtf? {"100-10":true,"200-20":true} <nil>
map must be: {"100-10":true, "200-20":true}
請(qǐng)注意,這就encoding.TextMarshaler足夠了,因?yàn)樵诰幗M為值時(shí)也會(huì)檢查它,而不僅僅是映射鍵。所以你不必同時(shí)實(shí)現(xiàn)encoding.TextMarshaler和json.Marshaler。
如果你同時(shí)實(shí)現(xiàn)了這兩者,當(dāng)值被編組為“簡單”值和映射鍵時(shí),你可以有不同的輸出,因?yàn)閖son.Marshaler在生成值時(shí)優(yōu)先:
func (a Int) MarshalJSON() ([]byte, error) {
test := a / 100
return json.Marshal(fmt.Sprintf("%d-%d", a, test))
}
func (a Int) MarshalText() (text []byte, err error) {
test := a / 10
return []byte(fmt.Sprintf("%d-%d", a, test)), nil
}
這次輸出將是(在Go Playground上試試):
array ["100-1","200-2"] <nil>
map wtf? {"100-10":true,"200-20":true} <nil>
map must be: {"100-10":true, "200-20":true}

TA貢獻(xiàn)1801條經(jīng)驗(yàn) 獲得超16個(gè)贊
接受的答案很好,但我不得不重新搜索足夠多的時(shí)間,所以我想通過示例給出關(guān)于編組/解組的完整答案,所以下次我可以只復(fù)制粘貼作為起點(diǎn):)
我經(jīng)常搜索的內(nèi)容包括:
將自定義類型編碼到 sql 數(shù)據(jù)庫
json 將 enum int 編碼為字符串
json編碼映射鍵但不編碼值
在這個(gè)例子中,我創(chuàng)建了一個(gè)自定義的 Weekday 類型,它匹配 time.Weekday int 值,但允許請(qǐng)求/響應(yīng) json 和數(shù)據(jù)庫中的字符串值
同樣的事情可以用任何使用 iota 的 int 枚舉來完成,以便在 json 和數(shù)據(jù)庫中具有人類可讀的值
游樂場測試示例:https://go.dev/play/p/aUxxIJ6tY9K
重要的一點(diǎn)在這里:
var (
// read/write from/to json values
_ json.Marshaler = (*Weekday)(nil)
_ json.Unmarshaler = (*Weekday)(nil)
// read/write from/to json keys
_ encoding.TextMarshaler = (*Weekday)(nil)
_ encoding.TextUnmarshaler = (*Weekday)(nil)
// read/write from/to sql
_ sql.Scanner = (*Weekday)(nil)
_ driver.Valuer = (*Weekday)(nil)
)
// MarshalJSON marshals the enum as a quoted json string
func (w Weekday) MarshalJSON() ([]byte, error) {
return []byte(`"` + w.String() + `"`), nil
}
func (w Weekday) MarshalText() (text []byte, err error) {
return []byte(w.String()), nil
}
func (w *Weekday) UnmarshalJSON(b []byte) error {
return w.UnmarshalText(b)
}
func (w *Weekday) UnmarshalText(b []byte) error {
var dayName string
if err := json.Unmarshal(b, &dayName); err != nil {
return err
}
d, err := ParseWeekday(dayName)
if err != nil {
return err
}
*w = d
return nil
}
// Value is used for sql exec to persist this type as a string
func (w Weekday) Value() (driver.Value, error) {
return w.String(), nil
}
// Scan implements sql.Scanner so that Scan will be scanned correctly from storage
func (w *Weekday) Scan(src interface{}) error {
switch t := src.(type) {
case int:
*w = Weekday(t)
case int64:
*w = Weekday(int(t))
case string:
d, err := ParseWeekday(t)
if err != nil {
return err
}
*w = d
case []byte:
d, err := ParseWeekday(string(t))
if err != nil {
return err
}
*w = d
default:
return errors.New("Weekday.Scan requires a string or byte array")
}
return nil
}
請(qǐng)注意,var 塊只是強(qiáng)制您正確地實(shí)現(xiàn)這些方法,否則它不會(huì)編譯。
另請(qǐng)注意,如果您排除MarshalJSON然后 go 將在它存在時(shí)使用MarshalText,因此如果您只希望鍵具有自定義編組但具有值的默認(rèn)行為那么您不應(yīng)該在您的主要類型上使用這些方法,而是具有僅用于映射鍵的包裝器類型
type MyType struct{}
type MyTypeKey MyType
var (
// read/write from/to json keys
_ encoding.TextMarshaler = (*MyTypeKey)(nil)
_ encoding.TextUnmarshaler = (*MyTypeKey)(nil)
)
func (w MyTypeKey) MarshalText() (text []byte, err error) {
return []byte(w.String()), nil
}
func (w *MyTypeKey) UnmarshalText(b []byte) error {
*w = MyTypeKey(ParseMyType(string(b)))
return nil
}
隨意改進(jìn)這個(gè)答案,我希望其他人發(fā)現(xiàn)它有幫助,我希望下次我能再次找到它并自己再次搜索它:)
- 2 回答
- 0 關(guān)注
- 108 瀏覽
添加回答
舉報(bào)