前言

平时工作当中经常使用json的序列化或者反序列,但是偶尔也会发现你定义的结构体使用json序列化或者反序列化的时候不是你期望的结果,我也经常遇到这种,所以我通过读源码之后总结了一些可能让人诧异的操作,希望能规避一些错误场景,而且也会有源码解释的哦。我的所有的示例代码都是可以点击查看的哦,可以在The Go Playground自己试试哦。(微信公众号文章不能直接点击,可以查看原文,在csdn里面点击跳转)

一、自定义序列化:MarshalJSON

下面代码你觉得结果是多少呢?{"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",    })}

二、自定义序列化:TextMarshaler

我们对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>类似标签的文本,在go中是如何处理的呢?

", ">", "&", 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是必须这么序列化的,那么如何不这样返回呢?办法是自定义序列化了。

四、数字如何序列化成字符串:{"age":18} => {"age":"18"}

有这种场景吗?例如前端要给年龄拼接成“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))    }}

只需要在json的tag中申明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这个匿名结构体加了tag,就多了一个层级,而不加就和go的匿名意思一样了,当做同级处理。这个还算正常吧?

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}

ProfileV1ProfileV22个结构体都有NickName这个字段,序列化的结构是啥样的呢?

{    "-": "Zhang San",    "Age": 18,    "Sex": true,    "HeadImg": "http://head.jpg",    "SelfIntroduce": "I am a gopher!"}

发现它直接把NickName忽略了,这我实在没想到竟然如此操作,而且我冲突了你至少报个错给我吧,但是go的json直接给你忽略了。。。。所以这里我觉得可以这么处理,给其中一个NickName打个标签重命名下。

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字段。

五、map的序列化支持的key类型

官方文档的解说:

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

七、最后说明下一些不被支持的结构体

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

总结

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

如果觉得有用,请关注我的公众号,会持续输出原创云原生相关文章

在这里插入图片描述

go json tag 字符串 整数_json:你或许还不知道的序列化操作(一)相关推荐

  1. C# json解析字符串总是多出双引号_json从立地到成佛

    本文原创作者bigsai(同公众号),本文以浅显易懂的方式给大家讲解自己所认知的json,如果有错误或者不准确地方还请大家给出指正,另外本文以关卡课程的方式在博学谷也是免费开放的,大家也可通过关卡方式 ...

  2. java url json字符串_使用HttpClient将URL中的JSON查询字符串发送到Web服务(Java)

    我有一个我建立的Web服务...我现在要做的是发送一个简单的请求,其中包含一个从Tapestry Web应用程序到该Web服务的json查询字符串.我四处搜索,大多数人都说使用Apache HttpC ...

  3. id jquery 拼接_jquery拼接ajax的json和字符串的方法

    整理文档,搜刮出一个jquery拼接ajax 的json和字符串拼接的代码,本文主要介绍了jquery拼接ajax 的json和字符串拼接的方法,这里整理了详细的代码,有需要的小伙伴可以参考下. jQ ...

  4. json 反序列化 父子类型_json类序列化与反序列化参考

    usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Web;usingSystem.Runtime.Ser ...

  5. FastJson对于JSON格式字符串、JSON对象及JavaBean之间的相互转换

    fastJson对于json格式字符串的解析主要用到了一下三个类: JSON:fastJson的解析器,用于JSON格式字符串与JSON对象及javaBean之间的转换. JSONObject:fas ...

  6. struts2操作json成字符串格式错误被转义及其前台访问json对象的方法

    这个问题确实纠结了我几天,是在是烦.网上也是参考了好几位大师的说法.都是不了了之. 这里记下这个问题,以备以后的参考. 我在struts2后台返回的是这样的json字符串: "[{\&quo ...

  7. ajax中json和文本的区别,json格式字符串--json对象 【ajax_responseText】重点

    JSON.parse(string) JSON.Stringify(object)document.getElementById("id").value=jsonObj    // ...

  8. Java实现向指定URL用POST方法发送Json格式字符串参数请求的工具类

    场景 SpringBoot项目中通过后台Controller向某服务接口发送POST请求. 实现 在项目pom.xml中添加依赖 <dependency><groupId>co ...

  9. go解析复杂json数组字符串:结合使用json和simple-json库

    原始json数组字符串 [{"addition_links": {"build_history": {"absolute": false,& ...

最新文章

  1. NOIP2018 模拟 9.11
  2. oracle用户权限的基本查询
  3. Spring集成web环境(使用封装好的工具)
  4. spring— Spring与Web环境集成
  5. java实现自动登录,并获取数据
  6. C++学习基础篇 —— 引用()的用法和应用
  7. ie下滚动条样式设置
  8. 2018.09.22 atcoder Integers on a Tree(构造)
  9. ATL-CComCreator(CComCoClass的使用)
  10. 让你事半功倍的小众 Python库
  11. HtmlHelper扩展 及 页面编码化
  12. SecureDrop 0.3,LibreOffice Online和更多新闻
  13. 2014全国计算机等级考试四级数据库工程师考试大纲,4月全国计算机等级考试四级数据库工程师笔试试卷(1)...
  14. Fastboot驱动及安装_我是亲民_新浪博客
  15. python小玩具(恶俗古风生成器)
  16. 使用VBA实现数据统计
  17. 重启docker卡死问题解决systemctl start docker卡住不动
  18. 网卡MAC地址相关信息大全(整理)(上)
  19. Android图片加载框架最全解析(七),实现带进度的Glide图片加载功能
  20. 类和对象系列教材 (一)- 什么是Java中的引用?

热门文章

  1. 10 年前的我 VS 10 年后的我
  2. 知道硬盘很慢,但没想到比 CPU Cache 慢 10000000 倍!
  3. iPhone 12 或10月13日发布;微信支付正计划加码存款市场;Swift正式登陆Win 10 | 极客头条
  4. AI 终极问题:我们的大脑是一台超级计算机吗?
  5. 颜值即正义!颜值爆表的几个数据交互的库来啦!
  6. 大陆集团ADAS招聘丨老司机带你现场体验自动驾驶的快感
  7. 开发小程序遇协同、平台兼容难题,该如何破局?
  8. 一个程序员开始优秀的 3 种迹象
  9. 00 后的 AI 开发者进阶之道:从入门到鏖战 MIT 编程大赛 | 人物志
  10. 求求你,下次面试别再问我什么是 Spring AOP 和代理了!