一、解析JSON
1、解析JSON有两种方法
- 解析到结构体中(需要提前知道结构体的结构);
- 解析到接口;
2、JSON数据与struct字段是如何相匹配的
- 首先查找tag(标签)中,含有Foo的可导出的struct字段(若标签不匹配,则匹配失败)
- 其次查找,字段名是Foo的导出字段
- 最后查找类似FOO或者FoO这样的除了首字母之外其他大小写不敏感的导出字段
注意:当接收到一个很大的JSON数据结构而你只想获取其中的部分数据的时候,只需将想要的数据对应的字段名大写,即可轻松解决这个问题。
3、匹配流程
tag
——Foo
——FOO或FoO或FOo
4、tag注意事项
- 字段的tag是“
-
”,直接忽略该字段 - tag中如果带有“
omitempty
”选项,那么如果该字段值为空,则忽略该字段(json编码时不会出现该字段) - 如果字段类型是bool,string,int,int64等,而tag中带有“
,string
”选项,那么这个字段在输出到JSON的时候会把该字段对应的值转换成JSON字符串
5、数据类型的对应
Bool 对应JSON布尔类型
float64 对应JSON数字类型
string 对应JSON字符串类型
[]interface{} 对应JSON数组 [1,2,3]
map[string]interface{} 对应JSON对象 {"name":"alan",age:18}
nil 对应JSON的null
复制代码
6、RawMessage
RawMessage类型是一个保持原本编码的json对象。本类型实现了Marshaler和Unmarshaler接口,用于延迟json的解码或者预计算json的编码。
type RawMessage []byte
本质是个byte数组,相当于json解析时,并没有解析RawMessage类型的字段,所以说起到了延迟json解析的作用
复制代码
二、注意
1、变量首字母要大写,才可导出
type Student struct {
Name string `json:"name"` //json标签
Age int `json:"age"`
School string `json:"school"`
Class int `json:"class"`
}
对于不可导出的字段,在编码和解码结果中,均不会出现;
{"name":"Alan","age":20,"school":"gzhu","class":10}//这就是标签的作用
复制代码
2、解码任意的数据
var f interface{}
err := json.Unmarshal(b, &f)
m := f.(map[string]interface{}) //也可以直接用map[string]interface{}接收,则省略了这步转化
for k, v := range m {//通过 for range 语法和 type switch 来访问其实际类型:
switch vv := v.(type) {
case string:
fmt.Println(k, "is string", vv)
case int:
fmt.Println(k, "is int", vv)
case []interface{}:
fmt.Println(k, "is an array:")
for i, u := range vv {
fmt.Println(i, u)
}
default:
fmt.Println(k, "is of a type I don’t know how to handle")
}
}
复制代码
3、数字默认转化为float64
由于 JSON 里的数字默认都会转成 Golang 的 float64 类型
4、性能问题
Go 语言里面原生支持了json的序列化以及反序列化,内部使用反射机制实现,性能有点差,在高度依赖 json 解析的应用里,往往会成为性能瓶颈。这时可以使用性能较好的第三方库;听说后续版本已经做了性能优化
5、编码指针类型
s := &Student{
Name: "Alan",
Age: 20,
School: "gzhu",
Class: 10,
}
data, err := json.Marshal(s)
//这样处理是合法的,结果也是正确的。
//遇到指针时,golang会自动的递归下降的进行格式转换。
复制代码
三、性能优良的第三方json包
package main
import (
"fmt"
//包别名,这样用起来跟系统包似的,代码都不用改了
json "github.com/json-iterator/go"
)
func main() {
val := []byte(`{"ID":1,"Name":"Reds","Colors":["Crimson","Red","Ruby","Maroon"]}`)
m := make(map[string]string)
json.Unmarshal(val, &m)//号称100%兼容标准库的json
fmt.Println(m)
//不用全部解析,单独取出某个字段
str := json.Get(val, "Colors", 0).ToString()
fmt.Println(str)
}
复制代码
四、解码中的一些坑
1、code不同,Data的数据类型也不同;
type CheckIPResult struct {
Code int `json:"code"`
Msg string `json:"msg"`
// Data interface{} `json:"data"`
Data json.RawMessage `json:"data"`
}
code != 0 {"msg": "miss param", "code": -1, "data": ""} //data是个空string
code = 0 {"msg": "", "code": 0, "data": {"123.163.183.200:18635": false, "27.209.164.24:15718": false}}
此例中,code的值决定了data字段的类型;
所以data需设计成interface{}类型,可接受任意类型;
或设计成json.RawMessage,延迟解析。先解析code,根据code的类型进一步解析json.RawMessage
复制代码
// CheckIPResult 测试IP结果
type CheckIPResult struct {
Code int `json:"code"`
Msg string `json:"msg"`
//Data interface{} `json:"data"`
Data json.RawMessage `json:"data"`
}
func main() {
resp := `
{
"msg": "",
"code": 0,
"data": {"123.163.183.200:18635": false, "27.209.164.24:15718": false}
}
`
// resp = `{"msg": "miss param", "code": -1, "data": ""}`
data := new(CheckIPResult)
err := json.Unmarshal([]byte(resp), &data)
if err != nil {
fmt.Println("json unmarshal failed,err:", err)
return
}
if data.Code == 0 {
//===========interface{}==============
//d, ok := data.Data.(map[string]interface{})
// //if !ok {
// // fmt.Println("类型转换失败")
// //}else{
// // fmt.Println(d)
// //}
//===============json.RawMessage================
d := make(map[string]bool)
err := json.Unmarshal(data.Data, &d)
if err != nil {
fmt.Println("json unmarshal failed,err:", err)
return
}
fmt.Println(d)
} else {
//===========interface{}==============
//d, ok := data.Data.(string)
//if !ok {
// fmt.Println("类型转换失败")
//}else{
// fmt.Println(d)
//}
//===============json.RawMessage================
d := ""
err := json.Unmarshal(data.Data, &d)
if err != nil {
fmt.Println("json unmarshal failed,err:", err)
return
}
fmt.Println(d)
}
}
复制代码
2、interface与具体类型的比较
var i interface{}
i = true
fmt.Println(i == true) //true
可将具体类型的值赋给interface,且此时的interface可以和具体来行就行比较;
反之,则不能将interface赋给具体类型
复制代码
有疑问加站长微信联系(非本文作者)