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
这种结构体在算法中还容易碰到,所以序列化要小心了。

通道、复合数据和方法都不能被序列化。

现在大概就这么总结下,后面我会对源码分析,如果碰到一些其他的坑,会再开一篇文章细细说说。

在这里插入图片描述