The Go Playground
{"MarshalJSON":"hit func MarshalJSON"}
代码如下:
type User struct { Name string Age int Sex bool ProfileV1 ProfileV2}func (u *User) MarshalJSON() ([]byte, error) { return json.Marshal(map[string]string{ "MarshalJSON": "hit func MarshalJSON", })}func main() { var zhangSan = User{ Name: "Zhang San", Age: 18, Sex: true, } bytes, err := json.Marshal(zhangSan) if err != nil { fmt.Println("error:", err) } else { fmt.Println("result:", string(bytes)) }}
运行得到的结果却是:
{"Name":"Zhang San","Age":18,"Sex":true,"HeadImg":"","SelfIntroduce":""}
如何修正?
第9行方法的接受者User不使用指针即可得到正确结果,代码如下:
func (u User) MarshalJSON() ([]byte, error) { return json.Marshal(map[string]string{ "MarshalJSON": "hit func MarshalJSON", })}
我们对MarshalJSON很熟,但是自定义序列化还有另外一种方式:TextMarshaler接口,把结构体能序列化成文本格式,不一定是json格式的文本。
官方说明:
TextMarshaler is the interface implemented by an object that canmarshal itself into a textual form.MarshalText encodes the receiver into UTF-8-encoded text and returns the result.
其实TextMarshaler也有MarshalJSON的问题,但是我这里想说的是当你的结构体同时实现了MarshalJSON和TextMarshaler之后,序列化的结果是什么呢?代码如下:
type User struct { Name string Age int Sex bool}func (u User) MarshalJSON() ([]byte, error) { return json.Marshal(map[string]string{ "MarshalJSON": "hit func MarshalJSON", })}func (u User) MarshalText() (text []byte, err error) { return []byte("MarshalText"), nil}func main() { var zhangSan = User{ Name: "Zhang San", Age: 18, Sex: true, } bytes, err := json.Marshal(zhangSan) if err != nil { fmt.Println("error:", err) } else { fmt.Println("result:", string(bytes)) }}
结果还是
{"MarshalJSON":"hit func MarshalJSON"}
所以这里就有个先后顺序了,当同时实现了MarshalJSON和TextMarshaler,那么只执行MarshalJSON。
去掉MarshalJSON之后,代码执行的结果为:
"MarshalText"
三、编码html标签
a>
", ">", "&", U+2028, and U+2029 are escaped to "\u003c","\u003e", "\u0026", "\u2028", and "\u2029".
会把"", "&", U+2028, and U+2029unicode编码。请看如下示例:
type User struct { Name string Age int Sex bool}func main() { var zhangSan = User{ Name: "Zhang San star", Age: 18, Sex: true, } bytes, err := json.Marshal(zhangSan) if err != nil { fmt.Println("error:", err) } else { fmt.Println("result:", string(bytes)) }}
结果为:
{"Name":"Zhang San \u003ca\u003estar\u003c/a\u003e","Age":18,"Sex":true}
为什么这么操作呢?官方解释是web浏览器的历史原因,会导致注入攻击。但是我们的场景不一定是返回给浏览器的,而go的json是必须这么序列化的,那么如何不这样返回呢?办法是自定义序列化了。
有这种场景吗?例如前端要给年龄拼接成“18岁”。
在go的json中如何使用呢?代码如下:
type User struct { Name string Age int `json:",string"` Sex bool}func main() { var zhangSan = User{ Name: "Zhang San", Age: 18, Sex: true, } bytes, err := json.Marshal(zhangSan) if err != nil { fmt.Println("error:", err) } else { fmt.Println("result:", string(bytes)) }}
string
1.加了json的tag的匿名结构体和没加tag的匿名结构体
加和不加序列化结果的json层级不一样,代码如下:
type User struct { Name string Age int Sex bool ProfileV1 `json:"profile_v1"` ProfileV2}func main() { var zhangSan = User{ Name: "Zhang San", Age: 18, Sex: true, ProfileV1: ProfileV1{ NickName: "Zhang Si(renamed)", HeadImg: "http://head.jpg", }, ProfileV2: ProfileV2{ NickName: "Zhang Wu(renamed)", SelfIntroduce: "I am a gopher!", }, } bytes, err := json.MarshalIndent(zhangSan, "", "\t") if err != nil { fmt.Println("error:", err) } else { fmt.Println(string(bytes)) }}type ProfileV1 struct { NickName string HeadImg string}type ProfileV2 struct { NickName string SelfIntroduce string}
打印出来结果如下:
{ "Name": "Zhang San", "Age": 18, "Sex": true, "profile_v1": { "NickName": "Zhang Si(renamed)", "HeadImg": "http://head.jpg" }, "NickName": "Zhang Wu(renamed)", "SelfIntroduce": "I am a gopher!"}
ProfileV1
2. "-"和"-,"
As a special case, if the field tag is "-", the field is always omitted.Note that a field with name "-" can still be generated using the tag "-,"
type User struct { Name string `json:"-,"` Age int Sex bool}func main() { var zhangSan = User{ Name: "Zhang San", Age: 18, Sex: true, } bytes, err := json.MarshalIndent(zhangSan, "", "\t") if err != nil { fmt.Println("error:", err) } else { fmt.Println(string(bytes)) }}
猜猜结果是多少:
{ "-": "Zhang San", "Age": 18, "Sex": true}
黑头搔更短,浑欲不胜簪
3.2个嵌套的匿名结构体有相同的字段
当同级有2个嵌套的匿名结构体有相同的字段的时候,序列化程啥样呢?当然如果有tag,我们就一tag的名作比较。
下面的代码结果是什么呢?
type User struct { Name string `json:"-,"` Age int Sex bool ProfileV1 ProfileV2}func main() { var zhangSan = User{ Name: "Zhang San", Age: 18, Sex: true, ProfileV1: ProfileV1{ NickName: "Zhang Si(renamed)", HeadImg: "http://head.jpg", }, ProfileV2: ProfileV2{ NickName: "Zhang Wu(renamed)", SelfIntroduce: "I am a gopher!", }, } bytes, err := json.MarshalIndent(zhangSan, "", "\t") if err != nil { fmt.Println("error:", err) } else { fmt.Println(string(bytes)) }}type ProfileV1 struct { NickName string HeadImg string}type ProfileV2 struct { NickName string SelfIntroduce string}
ProfileV1ProfileV2NickName
{ "-": "Zhang San", "Age": 18, "Sex": true, "HeadImg": "http://head.jpg", "SelfIntroduce": "I am a gopher!"}
NickNameNickName
4.匿名结构体的字段和匿名结构体所在结构体的字段相同
这里比较符合常理了,会使用匿名结构体所在结构体的字段,忽略匿名结构体的字段,代码如下:
type User struct { Name string NickName string Age int Sex bool ProfileV1 ProfileV2}func main() { var zhangSan = User{ Name: "Zhang San", NickName: "Bro San", Age: 18, Sex: true, ProfileV1: ProfileV1{ //NickName: "Zhang Si(renamed)", HeadImg: "http://head.jpg", }, ProfileV2: ProfileV2{ NickName: "Zhang Wu(renamed)", SelfIntroduce: "I am a gopher!", }, } bytes, err := json.MarshalIndent(zhangSan, "", "\t") if err != nil { fmt.Println("error:", err) } else { fmt.Println(string(bytes)) }}type ProfileV1 struct { //NickName string HeadImg string}type ProfileV2 struct { NickName string SelfIntroduce string}
结果如下:
{ "Name": "Zhang San", "NickName": "Bro San", "Age": 18, "Sex": true, "HeadImg": "http://head.jpg", "SelfIntroduce": "I am a gopher!"}
NickeName
官方文档的解说:
The map's key type must either be a string, an integer type,
or implement encoding.TextMarshaler. The map keys are sorted and used as
JSON object keys by applying the following rules,subject to the
UTF-8 coercion described for string values above:
- keys of any string type are used directly
- encoding.TextMarshalers are marshaled
- integer keys are converted to strings
string、integer(因为能直接转为string)、或者实现了接口encoding.TextMarshalers
func main() { bytes, err := json.MarshalIndent(map[bool]string{ true: "OK", }, "", "\t") if err != nil { fmt.Println("error:", err) } else { fmt.Println(string(bytes)) }}
结果为:
error: json: unsupported type: map[bool]string
User
type User struct { Name string NickName string Age int Sex bool Child *User}func main() { var zhangSan = &User{ Name: "Zhang San", NickName: "Bro San", Age: 18, Sex: true, } zhangSan.Child = zhangSan bytes, err := json.MarshalIndent(zhangSan, "", "\t") if err != nil { fmt.Println("error:", err) } else { fmt.Println(string(bytes)) }}
会导致什么错误呢?
error: json: unsupported value: encountered a cycle via *main.User
这种结构体在算法中还容易碰到,所以序列化要小心了。
通道、复合数据和方法都不能被序列化。
现在大概就这么总结下,后面我会对源码分析,如果碰到一些其他的坑,会再开一篇文章细细说说。