Golang | json

2,880 阅读6分钟

一、解析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赋给具体类型

本文使用 mdnice 排版