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+2029
unicode编码。请看如下示例:
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
这种结构体在算法中还容易碰到,所以序列化要小心了。
通道、复合数据和方法都不能被序列化。
现在大概就这么总结下,后面我会对源码分析,如果碰到一些其他的坑,会再开一篇文章细细说说。