附带一个 json 解析遇到的坑
经常会遇到 json 中的某个 key 是动态的情况, 比如下面这样:
1234567891011121314
{
"friends": [
{
"id": 0,
"name": "Robinson Woods"
}
],
"parent": [
{
"id": 1,
"name": "Alejandra Mcdaniel"
}
]
}
遇到这种, 只需要当成 map 类型来解析即可:
1234567891011121314151617181920212223242526272829303132
package main
import (
"encoding/json"
"log"
)
func main() {
data := `{"friends": [{"id": 0,"name": "Robinson Woods"}],"parent": [{"id": 1,"name": "Alejandra Mcdaniel"}]}`
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
dataMap := make(map[string][]User)
json.Unmarshal([]byte(data), &dataMap)
for k, v := range dataMap {
log.Printf(`%v,%v`, k, v)
}
}
而如果 json 返回格式不固定的时候, 这时就应该使用json.RawMessage 类型了, 官方解释如下
RawMessage is a raw encoded JSON value. It implements Marshaler and Unmarshaler and can be used to delay JSON decoding or precompute a JSON encoding.
大致意思就是暂时不解析这部分, 之后这部分可以进行二次解析, 最适合那种返回的内容格式不固定的情况. 之后的处理, 如果知道固定的几种格式可以通过断言来判断返回的是哪种, 不然的话就只能断言成 interface 类型然后一层层解析了.
而更复杂一点的情况, 动态 key 后面可能是一个没有 key 的数组, 这种目前 go 不支持, 只能通过类型断言来解决了
断言注意事项
interface{}类型在Unmarshal时,会自动将JSON转换为对应的数据类型:
1234567891011
JSON的boolean 转换为bool
JSON的数值 转换为float64
JSON的字符串 转换为string
JSON的Array 转换为[]interface{}
JSON的Object 转换为map[string]interface{}
JSON的null 转换为nil
所以如果你已经把 json 解析过一次了,是不可能再断言成 struct 类型的, struct统统变成了 map 类型, 这时有俩办法, 在解析时就用对应的 struct 类型,而不是写成 interface, 第二种就是把 map 再 Marshal 成 byte数组 类型, 然后再Unmarshal回 struct, 不用脑子想就知道不要用第二种…..
踩坑
json解析tag不区分大小写
遇到过各种奇奇怪怪的 json ,在解析下面这种, key 仅仅大小写不一样的时候就要注意了, 如果你平时写 struct 喜欢简洁, 就是用不到的字段不写的话, 这时可能就有问题了, 因为 标准库的json在解析 tag 时,是不区分大小写的, 会出现如下图的覆盖情况
忽略 json 字段别写错 tag
json:"omitempty" tag 的意思是如果该值为该类型的零值时会忽略,
想完全忽略某个 json 字段返回,应该使用json:"-"