前言

上一次我们一起学习了如何解析接口返回的XML数据,这一次我们一起来学习JSON的解析方法。

JSON(Javascript Object Notation)是一种轻量级的数据交换语言,以文字为基础,具有自我描述性且易于让人阅读。XML是一个完整的标记语言,而JSON不是。JSON比XML更小、更快,更易解析,因此其被广泛应用于网络数据传输领域。

Go语言的标准库已经非常好的支持了JSON,可以很容易的对JSON数据进行编、解码的工作。下面我们通过一些实例一起来学习。

首先我们假设我们的接口返回的JSON数据如下:

{   "code": "00",
    "message": "SUCCESS",
    "describe": "成功",
    "resultInfo": { "uniqueNumber": "201808161133401673324075025000035" } 
}

解析到结构体

这种方式与xml解析的方法基本没有什么区别,我们首先要定义一个结构体,然后调用json.Unmarshal把数据解析到结构体:

func StructMethod() {
    resp := `{"code": "00",
              "message": "SUCCESS",
              "describe": "成功",
              "resultInfo": { "uniqueNumber": "201808161133401673324075025000035" } 
             }`

    type JsonResp struct {
        Code       int               `json:"code"`
        Message    string            `json:"message"`
        Describe   string            `json:"describe"`
        ResultInfo map[string]string `json:"resultInfo"`
    }
    var smsresp JsonResp
    temp := []byte(resp)
    errs := json.Unmarshal(temp, &smsresp)
    if errs != nil {
        return
    }
    fmt.Println(smsresp.Code)
    fmt.Println(smsresp.Describe)
    fmt.Println(smsresp.Message)
    fmt.Println(smsresp.ResultInfo["uniqueNumber"])
}

json数据与struct字段是如何相匹配的呢?

可能有的小伙伴和我一样好奇,在解析的时候,json数据与struct字段是如何相匹配的呢?例如JSON的key是code,那么怎么找对应的字段呢?

  • 首先查找tag含有code的可导出的struct字段(首字母大写)
  • 其次查找字段名是code的导出字段
  • 最后查找类似Code或者COde这样的除了首字母之外其他大小写不敏感的导出字段

注意: 能够被赋值的字段必须是可导出字段(即首字母大写)。同时JSON解析的时候只会解析能找得到的字段,找不到的字段会被忽略。我们在实际使用的过程中一定要随时警惕这一点。

其实与这个潜在的坑相比,它的优势非常明显:当你接收到一个很大的JSON数据结构而你却只想获取其中的部分数据的时候,你只需将你想要的数据对应的字段名大写,即可轻松解决。

解析到interface

上面那种解析方式是在我们知晓被解析的JSON数据的结构的前提下采取的方案,如果我们不知道被解析的数据的格式,又应该如何来解析呢?

Go类型和JSON类型

我们知道interface{}可以用来存储任意数据类型的对象,这种数据结构正好用于存储解析的未知结构的json数据的结果。JSON包中采用map[string]interface{}和[]interface{}结构来存储任意的JSON对象和数组。Go类型和JSON类型的对应关系如下:

类型

JSON类型

bool

JSON booleans,

float64

JSON numbers,

string

JSON strings,

nil

JSON null.

实例代码

// InterfaceMethod 方式
func InterfaceMethod() {
    resp := `{"code": "00",
    "message": "SUCCESS",
    "describe": "成功",
    "resultInfo": {"uniqueNumber": "201808161133401673324075025000035"} 
  }`

    var x interface{}
    _ = json.Unmarshal([]byte(resp), &x)
    m := x.(map[string]interface{})

    for k, v := range m {
        switch vv := v.(type) {
        case string:
            fmt.Println(k, "is string", vv)
        case int:
            fmt.Println(k, "is int", vv)
        case float64:
            fmt.Println(k, "is float64", vv)
        case []interface{}:
            fmt.Println(k, "is an array:")
            for i, u := range vv {
                fmt.Println(i, u)
            }
        case map[string]interface{}:
            fmt.Println(k, "is an map[string]string:")
            for i, u := range vv {
                fmt.Println(i, u)
            }
        default:
            fmt.Println(k, "is of a type didn't handle")
        }
    }
}

simpleJson

上面两种方式其实已经能应付我们一般的工作了,但是单纯就解析数据而言(这里先买个关子),其实还有一种更简单的方式,那就是第三方库:github.com/bitly/go-simplejson,使用方法如下:

func SimplejsonMethod() {
    resp := `{"code": "00",
              "message": "SUCCESS",
              "describe": "成功",
              "resultInfo": { "uniqueNumber": "201808161133401673324075025000035" } 
            }`
    js, errs := NewJson([]byte(resp))
    if errs != nil {
        return
    }
    discount := js.Get("resultInfo").Get("uniqueNumber")
    strcode, _ := js.Get("code").String()
    intcode, _ := js.Get("code").Int()
    path := js.GetPath("resultInfo", "uniqueNumber")
    fmt.Println(discount)
    fmt.Println(strcode)
    fmt.Println(intcode)
    fmt.Println(path)
}

运行输出:

bingo@Mac unpackData$ go run JSONparse.go
&{201808161133401673324075025000035}
00
0
&{201808161133401673324075025000035}

GetPath方法是Get的人精简版,使用结尾调用的方法可以吧输出的结果转化为指定的类型(string 00 转换为 0 )。

那么多个方法的调用怎么完成数据传递的呢?输出结果的&符号其实已经暴露了它的身份,对就是指针,这个库每个方法接收和返回的都是指针数据。

总结

  • json 解析 到struct
  • interface{}与type assert的配合使用
  • simplejson
  • 学习一门语言是一个慢慢熟悉的过程,初始是不易使用完成度太高的轮子