go json tag 字符串 整数_json:你或许还不知道的序列化操作(一)
前言
平时工作当中经常使用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+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是必须这么序列化的,那么如何不这样返回呢?办法是自定义序列化了。
四、数字如何序列化成字符串:{"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
}
ProfileV1
和ProfileV2
2个结构体都有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:你或许还不知道的序列化操作(一)相关推荐
- C# json解析字符串总是多出双引号_json从立地到成佛
本文原创作者bigsai(同公众号),本文以浅显易懂的方式给大家讲解自己所认知的json,如果有错误或者不准确地方还请大家给出指正,另外本文以关卡课程的方式在博学谷也是免费开放的,大家也可通过关卡方式 ...
- java url json字符串_使用HttpClient将URL中的JSON查询字符串发送到Web服务(Java)
我有一个我建立的Web服务...我现在要做的是发送一个简单的请求,其中包含一个从Tapestry Web应用程序到该Web服务的json查询字符串.我四处搜索,大多数人都说使用Apache HttpC ...
- id jquery 拼接_jquery拼接ajax的json和字符串的方法
整理文档,搜刮出一个jquery拼接ajax 的json和字符串拼接的代码,本文主要介绍了jquery拼接ajax 的json和字符串拼接的方法,这里整理了详细的代码,有需要的小伙伴可以参考下. jQ ...
- json 反序列化 父子类型_json类序列化与反序列化参考
usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Web;usingSystem.Runtime.Ser ...
- FastJson对于JSON格式字符串、JSON对象及JavaBean之间的相互转换
fastJson对于json格式字符串的解析主要用到了一下三个类: JSON:fastJson的解析器,用于JSON格式字符串与JSON对象及javaBean之间的转换. JSONObject:fas ...
- struts2操作json成字符串格式错误被转义及其前台访问json对象的方法
这个问题确实纠结了我几天,是在是烦.网上也是参考了好几位大师的说法.都是不了了之. 这里记下这个问题,以备以后的参考. 我在struts2后台返回的是这样的json字符串: "[{\&quo ...
- ajax中json和文本的区别,json格式字符串--json对象 【ajax_responseText】重点
JSON.parse(string) JSON.Stringify(object)document.getElementById("id").value=jsonObj // ...
- Java实现向指定URL用POST方法发送Json格式字符串参数请求的工具类
场景 SpringBoot项目中通过后台Controller向某服务接口发送POST请求. 实现 在项目pom.xml中添加依赖 <dependency><groupId>co ...
- go解析复杂json数组字符串:结合使用json和simple-json库
原始json数组字符串 [{"addition_links": {"build_history": {"absolute": false,& ...
最新文章
- NOIP2018 模拟 9.11
- oracle用户权限的基本查询
- Spring集成web环境(使用封装好的工具)
- spring— Spring与Web环境集成
- java实现自动登录,并获取数据
- C++学习基础篇 —— 引用()的用法和应用
- ie下滚动条样式设置
- 2018.09.22 atcoder Integers on a Tree(构造)
- ATL-CComCreator(CComCoClass的使用)
- 让你事半功倍的小众 Python库
- HtmlHelper扩展 及 页面编码化
- SecureDrop 0.3,LibreOffice Online和更多新闻
- 2014全国计算机等级考试四级数据库工程师考试大纲,4月全国计算机等级考试四级数据库工程师笔试试卷(1)...
- Fastboot驱动及安装_我是亲民_新浪博客
- python小玩具(恶俗古风生成器)
- 使用VBA实现数据统计
- 重启docker卡死问题解决systemctl start docker卡住不动
- 网卡MAC地址相关信息大全(整理)(上)
- Android图片加载框架最全解析(七),实现带进度的Glide图片加载功能
- 类和对象系列教材 (一)- 什么是Java中的引用?
热门文章
- 10 年前的我 VS 10 年后的我
- 知道硬盘很慢,但没想到比 CPU Cache 慢 10000000 倍!
- iPhone 12 或10月13日发布;微信支付正计划加码存款市场;Swift正式登陆Win 10 | 极客头条
- AI 终极问题:我们的大脑是一台超级计算机吗?
- 颜值即正义!颜值爆表的几个数据交互的库来啦!
- 大陆集团ADAS招聘丨老司机带你现场体验自动驾驶的快感
- 开发小程序遇协同、平台兼容难题,该如何破局?
- 一个程序员开始优秀的 3 种迹象
- 00 后的 AI 开发者进阶之道:从入门到鏖战 MIT 编程大赛 | 人物志
- 求求你,下次面试别再问我什么是 Spring AOP 和代理了!