JSON(Javascript Object Notation)是一种轻量级的数据交换语言,以文字为基础,具有自我描述性且易于阅读。尽管 JSON 是 JavaScript 的一个子集,但 JSON 是独立于语言的文本格式,并且采用了类似于 C 语言家族的一些习惯。JSON 与 XML 最大的不同在于 XML 是一个完整的标记语言,而 JSON 不是。JSON由于比 XML 更小、更快,更易解析,以及浏览器的內建支持,使得其更适用于网络数据传输领域。

Golang 自带的 JSON 解析库 encoding/json,可以用来将结构化数据序列化成 json 字符串或从 json 字符串中解析出我们想要的数据。

1.解析 json

1.1 map[string]interface{} 存储 json

给一个较为复杂的 json 字符串,包含数组,数组的元素是 json 对象。我们需要取出数组第一个元素中的某一个字段值。其它的解析均可参考下面的代码。

package mainimport ("encoding/json""fmt"
)func main() {jsonStr := []byte(`{"uin":1589276509,"feedID":10000,"videos":[{"picture":"http://qqpublic.qpic.cn/avatar.jpg","duration":"839"}]}`)var jsonMap map[string]interface{}if err := json.Unmarshal(jsonStr, &jsonMap); err!=nil {fmt.Printf("json decode failed, err=%v", err)return}if  value, ok := jsonMap["videos"]; ok {fmt.Printf("value=%#v\n", value)if sliceValue, ok := value.([]interface{}); ok {if mapValue, ok := sliceValue[0].(map[string]interface{}); ok {if duration, ok := mapValue["duration"]; ok {fmt.Printf("d=%v,type=%T\n", duration, duration)}}}}
}

程序输出:

value=[]interface {}{map[string]interface {}{"picture":"http://qqpublic.qpic.cn/avatar.jpg", "duration":"839"}}
d=839,type=string

解析 json 字符串时,需要注意如下几点:
(1)Golang 类型和 JSON 类型的对应关系如下:

map[string]interface{} 代表 JSON 对象
[]interface{} 代表 JSON 数组
bool 代表 JSON boolean
float64 代表 JSON number
string 代表 JSON string
nil 代表 JSON null

1.2 struct 存储 json

如果我们事先知道 JSON 串,那么可以指定具体的 struct 来存储解析后的 json。可以通过在线工具Golang: Convert JSON to Struct快速将 JOSN 转为 Go struct。

package mainimport ("fmt""encoding/json"
)type FeedsItem struct {Title             string   `json:"string_title"`Score             int      `json:"uint32_score"`AiKongbuLevel     int      `json:"uint32_ai_kongbu_level"`AiDisuLevel       int      `json:"uint32_ai_disu_level"`AiBiaotidangLevel int      `json:"uint32_ai_biaotidang_level"`AiUnsocialLevel   int      `json:"uint32_ai_unsocial_level"`
}func main() {data:=`[{"uint32_feed_pos": 1,"string_title": "它才是“天然补血王”,女性隔三差五吃,美容驻颜,气色更红润","uint32_score": 4,"uint32_ai_kongbu_level": 0,"uint32_ai_disu_level": 0,"uint32_ai_biaotidang_level": 1,"uint32_ai_unsocial_level": 0}, {"uint32_feed_pos": 2,"string_title": "7岁的火星男孩,顺利通过测谎仪,现在为何消失不见了?","uint32_score": 4,"uint32_ai_kongbu_level": 0,"uint32_ai_disu_level": 0,"uint32_ai_biaotidang_level": 0,"uint32_ai_unsocial_level": 0}]`var feedsInfo []FeedsItem//第二个参数必须是指针,否则无法接收解析后的数据if err := json.Unmarshal([]byte(data), &feedsInfo); err != nil {fmt.Printf("json.Unmarshal() failed, err=%v data=%s\n", err, data)return}fmt.Printf("jsonMap=%#v\n", feedsInfo)	
}

编译运行输出:

jsonMap=[]main.FeedsItem{main.FeedsItem{Title:"它才是“天然补血王”,女性隔三差五吃,美容驻颜,气色更红润", Score:4, AiKongbuLevel:0, AiDisuLevel:0, AiBiaotidangLevel:1, AiUnsocialLevel:0}, main.FeedsItem{Title:"7岁的火星男孩,顺利通过测谎仪,现在为何消失不见了?", Score:4, AiKongbuLevel:0, AiDisuLevel:0, AiBiaotidangLevel:0, AiUnsocialLevel:0}}

1.3 []map[string]interface{} 解析 json 数组

如果 json 字符串是一个 json 数组,除了可以使用 struct 来解析,也可以使用 []map[string]interface{} 来解析。

package mainimport ("fmt""encoding/json"
)func main() {s := `[{"id":300001,"exp_layer":"string","buckets":"1,2,3"},{"id":300002,"exp_layer":"test2","buckets":"4"}]`var jsonArray []map[string]interface{}if err := json.Unmarshal([]byte(s), &jsonArray); err!=nil {fmt.Printf("json decode failed, err=%v value=%v\n", err, s)return}   fmt.Printf("jsonArray=%+v\n", jsonArray)
}

输出结果:

jsonArray=[map[exp_layer:string buckets:1,2,3 id:300001] map[id:300002 exp_layer:test2 buckets:4]]
2.生成 JSON

解析 json 时,可以使用 map[string]interface{} 或者 struct 来存储解析后的数据。同样地,我们可以将任意 Golang 对象序列化为 JSON。

Golang 中,JSON 对象表示主要有两种方式,一种是 struct,另一种是 map[string]interface{}。在表示数组时,分别使用具体类型的切片和任意类型的切片 []interface{}。

2.1 struct 序列化为 json

假设我们有如下一个结构体 Student 及其一个实例对象 st,将其序列化为 json,具体实现如下:

package mainimport ("encoding/json""fmt"
)type Student struct {Name 		string `json:"name"`Age   		intgender   	stringClass 		*Class `json:"class"`
}type Class struct {Name  stringGrade int
}func main() {//实例化一个数据结构,用于生成 json 字符串st := Student{Name: "张三",Age:  18,gender:  "男",}//指针变量cla := new(Class)cla.Name = "1班"cla.Grade = 3st.Class=cla//Marshal失败时 err != niljsonStu, err := json.Marshal(st)if err != nil {fmt.Println("生成json字符串错误")}//jsonStu是[]byte类型,转化成string类型便于查看fmt.Println(string(jsonStu))
}

程序输出结果:

{"name":"张三","Age":18,"class":{"Name":"1班","Grade":3}}
json:"name"

2.2 map[string]interface{} 序列化为 json

package mainimport ("encoding/json""fmt"
)func main() {m := make(map[string]interface{})m["Name"]="张三"m["Age"]=18m["Gender"]="男"jsonObject := make(map[string]interface{})jsonObject["Name"]="1班"jsonObject["Grade"]=3m["Class"]=jsonObject//将 map[string]interface{} 转换为 jsonsJson, _ := json.Marshal(m)fmt.Printf("sJson=%v\n", string(sJson))
}

编译运行输出:

sJson={"Age":18,"Class":{"Grade":3,"Name":"1班"},"Gender":"男","Name":"张三"}

2.3 一个较为复杂的例子

假如需要生成如下一个较为复杂的 json:

{"requests": [{"positions": [{"ads": [{"bid_result": "1","ecpm": "1.1","view_id": "Y940raTWuc2bAbPtq1lEc"}, {"bid_result": "2","ecpm": "2.2","view_id": "L37Qw!KwBORErDPbViQ"}]}]}]
}

其中,requests 为一个数组,数组的元素是一个 json 对象,该对象只有一个成员 positions;positions 也是一个数组,数组的元素也是一个 json 对象,该对象也只有一个成员 ads;ads 同样还是数组,数组元素还是一个 json 对象。

2.3.1 使用 struct + slice

因为在 Golang 中,一个 json 对象可以使用 struct 表示,json 中的数组可以使用 slice 来表示,于是我们可以使用 struct 和 slice 来表示上面的 json。

package mainimport ("encoding/json""fmt"
)type Request struct{Requests []RequestValue	`json:"requests"`
}type RequestValue struct {Positions []PositionValue `json:"positions"`
}type PositionValue struct {Ads []AdsValue `json:"ads"`
}type AdsValue struct {Bid_result string       `json:"bid_result"`    Ecpm string             `json:"ecpm"`View_id string          `json:"view_id"`
}func main() {positionValue := PositionValue{Ads: []AdsValue{AdsValue{Bid_result:"1",Ecpm:"1.1",View_id:"Y940raTWuc2bAbPtq1lEc",},AdsValue{Bid_result:"2",Ecpm:"1.2",View_id:"L37Qw!KwBORErDPbViQ",},},}requestValue := RequestValue{Positions: []PositionValue{positionValue},}request := Request{Requests: []RequestValue{requestValue}}//将 struct 转换为 jsonsJson, _ := json.Marshal(request)fmt.Printf("sJson=%v\n", string(sJson))
}

编译运行输出:

sJson={"requests":[{"positions":[{"ads":[{"bid_result":"1","ecpm":"1.1","view_id":"Y940raTWuc2bAbPtq1lEc"},{"bid_result":"2","ecpm":"1.2","view_id":"L37Qw!KwBORErDPbViQ"}]}]}]}

2.3.2 使用 map[string]interface{} + []interface{}

同样地,可以使用 map[string]interface{} 来表示任意的 json,如果元素是数组,可以使用 []interface{}。

package mainimport ("encoding/json""fmt"
)func main() {//json 对象    adValue := make(map[string]interface{})adValue["bid_result"]="1"adValue["ecpm"]="1.1"adValue["view_id"]="Y940raTWuc2bAbPtq1lEc"adValue1 := make(map[string]interface{})adValue1["bid_result"]="2"adValue1["ecpm"]="1.2"adValue1["view_id"]="L37Qw!KwBORErDPbViQ"//json 对象,只有一个成员 ads,且 ads 是一个 json 对象数组ads := make(map[string]interface{})ads["ads"]=[]interface{}{adValue, adValue1}//json 对象,只有一个成员 positions,且 positions 是一个 json 对象数组positions := make(map[string]interface{})positions["positions"]=[]interface{}{ads}//json 对象,只有一个成员 requests,且 requests 是一个 json 对象数组requests := make(map[string]interface{})requests["requests"] = []interface{}{positions}//将 map[string]interface{} 转换为 jsonsJson, _ := json.Marshal(requests)fmt.Printf("sJson=%v\n", string(sJson))
}

编译运行输出:

sJson={"requests":[{"positions":[{"ads":[{"bid_result":"1","ecpm":"1.1","view_id":"Y940raTWuc2bAbPtq1lEc"},{"bid_result":"2","ecpm":"1.2","view_id":"L37Qw!KwBORErDPbViQ"}]}]}]}

输出结果与使用 struct 相同。相比于 struct,使用 map[string]interface{} 更加的简洁,因为省去了 struct 的定义。


参考文献

[1] Golang中文官网.Package json
[2] Go的json解析:Marshal与Unmarshal
[3] stackoverflow.panic: json: cannot unmarshal array into Go value of type main.Structure
[4] Introducing JSON