Go中使用json很多,玩法也很多,整理了一下go中关于json的tag使用
包含了一些遇到的问题与解决方案
A field declaration may be followed by an optional string literal tag, which becomes an attribute for all the fields in the corresponding field declaration. The tags are made visible through a reflection interface but are otherwise ignored.
官方的解释是这个标签信息可以通过反射获取,自定义一些规则,比如常见的db json
Tag是一个以反引号 `
包围, 空格分隔的 k:v
对,常用于数据解析,关系映射等
Go中数据类型与Json支持的类型对应关系
1 | bool, for JSON booleans |
1 | package main |
指定json字段名
User
结构体中,json
标签指定了它的输出字段名,执行将输出
1 | {"age":20,"name":"老王","phone":["18888888888","15555555555"],"skill":null,"birthday":"2000-01-01T10:08:00+08:00"} |
输出了null
切片、map、等零值为nil
,对应json的值为null
如果要输出[]
则要赋值为空切片
1 | // someOne赋值为 |
要注意的是空map对应的json输出为 {} 而非 []
忽略零值字段
通过设置tag添加omitempty
,当字段值为其类型对应的零值时,可达到忽略零值字段的目的。
在输出结果中Address
被设置为空字符串,也未被输出,因为string
类型的零值即为""
忽略指定字段
User
结构体中Password
的tag被指定为-
所以,即便被设置了具体的值,在json中也未被输出
结构体嵌套输出
比如将用户的技能和新增的个人主页地址,并将一些展示信息独立为Profile
结构
```go
type Profile struct {
Phone []string json:"phone,omitempty"
Skill []string json:"skill,omitempty"
Birthday time.Time json:"birthday,omitempty"
HomePage string json:"url"
Address string json:"address,omitempty"
}
type User struct {
Profile
Age uint8 `json:"age"`
Name string `json:"name"`
Password string `json:"-"`
}
// someOne 赋值为
someOne := User{
Profile: Profile{
Skill: []string{“吃”,”喝”,”玩”,”乐”},
Birthday: birthday,
},
Age: 20,
Name: "老王",
Password: "admin123",
}
// 对应输出为
{“skill”:[“吃”,”喝”,”玩”,”乐”],”birthday”:”2000-01-01T10:08:00+08:00”,”url”:””,”age”:20,”name”:”老王”}
1 |
|
输出为
1 | {"profile":{"skill":["吃","喝","玩","乐"],"birthday":"2000-01-01T10:08:00+08:00","url":""},"age":20,"name":"老王"} |
忽略嵌套结构体
仅设置omitempty
是无法屏蔽嵌套字段的输出的
1 | type User struct { |
输出为
1 | {"profile":{"birthday":"0001-01-01T00:00:00Z","url":""},"age":20,"name":"老王"} |
解决方案为使用嵌套结构体指针
1 | type User struct { |
输出为
1 | {"age":20,"name":"老王"} |
忽略字段输出(不修改原结构体)
比如要忽略Name
输出,利用的是定义相同json tag来完成
可使用任意类型,空struct有利于节省内存及统一
1 | type Omit *struct{} |
临时使用的话,一个匿名结构体也可以
json.RawMessage 延迟解析
可通过一个结构体灵活匹配多种类型定义
1 | type MultiType struct { |
可以通过先匹配type再对应解析数据,如
1 | { |
json传递数字
json的数字,对应的是go的float64类型,json的规范中,对于数字类型,并不区分是整型还是浮点型
这就意味着只有显式的指定字段类型,go才会将数字转换,所以如果超出float64精度范围的数字,就需要特殊处理,否则会产生精度损失,比如将数字类型反序列化为interface{}
类型时,很容易踩坑
另外,如果字段定义为数字类型,int int8
等,但入参为字符串"42"
,会解析错误
以上问题可通过使用json.Number
类型解决,见参考链接
浏览器中对int64处理异常主要原因:
JavaScript 采用双精度浮点数( IEEE 754 标准)来表示它的 Number 类型,一个数字占用 64 bits
第一位 0 表示正值、1 表示负值;第 2- 12 位表示 2 的指数部分(可正可负);剩下的 52 个 bits 表示尾数部分
所以js中最大安全整数为 2 的 53 次方减 1 (9007199254740991)
涉及与前端或不同语言进行交互的,json中的int最好使用字符串来避免精度问题
1 | func main() { |
1 | type User struct { |
如果确定入参类型只为字符串类型数字,也可通过tag中指定类型来解决
通过这种定义,输出的数字也将是字符串类型
1 | type User struct { |
自定义规则
1 | type Marshaler interface { |
通过实现以上两个接口,达到自定义json格式或解析的目的
比如修改time的输出格式以及兼容各种格式的时间字符串解析,时间戳的转换等
扩展类型的方式或者内嵌方式
1 | type CustomTime struct { |