我无法理解自定义元帅int
到string
的奇怪行为。
这里是一个例子:
package main
import (
"encoding/json"
"fmt"
)
type Int int
func (a Int) MarshalJSON() ([]byte, error) {
test := a / 10
return json.Marshal(fmt.Sprintf("%d-%d", a, test))
}
func main() {
array := []Int{100, 200}
arrayJson, _ := json.Marshal(array)
fmt.Println("array", string(arrayJson))
maps := map[Int]bool{
100: true,
200: true,
}
mapsJson, _ := json.Marshal(maps)
fmt.Println("map wtf?", string(mapsJson))
fmt.Println("map must be:", `{"100-10":true, "200-20":true}`)
}
输出为:
array ["100-10","200-20"]
map wtf? {"100":true,"200":true}
map must be: {"100-10":true, "200-20":true}
https://play.golang.org/p/iiUyL2Hc5h_P
我想念什么?
答案 0 :(得分:6)
这是预期的结果,记录在json.Marshal()
:
地图值编码为JSON对象。映射的键类型必须是字符串,整数类型或实现encoding.TextMarshaler。通过应用以下规则对映射键进行排序并用作JSON对象键,但要遵守上面针对字符串值所述的UTF-8强制:
- string keys are used directly - encoding.TextMarshalers are marshaled - integer keys are converted to strings
请注意,映射键与属性值的处理方式不同,因为JSON中的映射键是始终为string
值的属性名称(而属性值可以是JSON文本,数字和布尔值)。
根据文档,如果您希望它也适用于地图键,请实施encoding.TextMarshaler
:
func (a Int) MarshalText() (text []byte, err error) {
test := a / 10
return []byte(fmt.Sprintf("%d-%d", a, test)), nil
}
(请注意,MarshalText()
应该返回“仅”简单文本,而不是JSON文本,因此我们省略了JSON封送处理!)
这样,输出将是(在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}
请注意,encoding.TextMarshaler
就足够了,因为在封送为值时不仅会检查地图键,还会检查它。因此,您不必同时实现encoding.TextMarshaler
和json.Marshaler
。
如果您同时实现这两种方法,则在将值分别编组为“简单”值和映射键时,可能会有不同的输出,因为json.Marshaler
在生成值时优先:
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}