前言

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

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

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

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

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

解析到结构体

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
  • 学习一门语言是一个慢慢熟悉的过程,初始是不易使用完成度太高的轮子