
JSON操作
1.JSON介绍
JSON (JavaScript Object Notation)是一种比XML更轻量级的数据交换格式,在易于人们阅读和编写的同时,也易于程序解析和生成。尽管JSON是JavaScript的一个子集,但JSON采用完全独立于编程语言的文本格式,且表现为键/值对集合的文本描述形式(类似一些编程语言中的字典结构),这使它成为较为理想的、跨平台、跨语言的数据交换语言。
{ "Company":"Lianshi", "Subjects":[ "Go", "Web", "Python", "C++" ], "IsOk":true, "Price":8980.88 }开发者可以用JSON传输简单的字符串、数字、布尔值,也可以传输一个数组,或者一个更复杂的复合结构,在Web开发领域中,JSON被广泛应用于Web服务端程序和客户端之间的数据通信。
JSON有4种基本类型:Strings,numbers,booleans,null 对应Go的string,float64,bool,nil
JSON有2种结构类型:对象{"name":"Jim","age":18}和数组[1,2,3]
2.JSON编码
Go语言内建对JSON的支持。使用Go语言内置的encoding/json 标准库,开发者可以轻松使用Go程序生成和解析JSON格式的数据。
使用json.Marshal()函数可以对一组数据进行JSON格式的编码。
- json.Marshal()函数的声明如下
func Marshal(v interface{}) ([]byte, error)
- 格式化输出
// MarshalIndent 很像 Marshal,只是用缩进对输出进行格式化 func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)
2.1通过结构体生成JSON
- json.Marshal()标准输出
package main import ( "encoding/json" "fmt" ) type Student struct { Name string Age int Score []int Addr string } func main() { stu := Student{"x86", 18, []int{100, 100, 20}, "北京朝阳区"} //生成 json 数据格式 slice1, err := json.Marshal(stu) fmt.Print(string(slice1)) //分割线 fmt.Println("\n--分割线--") //生成 格式化json 数据格式 slice2, err := json.MarshalIndent(stu, "", " ") if err != nil { fmt.Println("json格式转化失败") } else { fmt.Print(string(slice2)) } } $ go run project.go {"Name":"x86","Age":18,"Score":[100,100,20],"Addr":"北京朝阳区"} --分割线-- { "Name": "x86", "Age": 18, "Score": [ 100, 100, 20 ], "Addr": "北京朝阳区" }
- json.MarshalIndent()格式化输出
package main import ( "encoding/json" "fmt" ) type IT struct { Company string Subjects []string IsOk bool Price float64 } func main() { t1 := IT{"Lianshi", []string{"Go", "Web", "Python", "C++"}, true, 8980.88} //生成一段JSON格式的文本 //如果编码成功, err 将赋于零值 nil,变量b 将会是一个进行JSON格式化之后的[]byte类型 b, err := json.MarshalIndent(t1, "", " ") if err != nil { fmt.Println("json err:", err) } fmt.Println(string(b)) } $ go run project.go { "Company": "Lianshi", "Subjects": [ "Go", "Web", "Python", "C++" ], "IsOk": true, "Price": 8980.88 }
2.2通过map生成JSON
- json.Marshal()
// 创建一个保存键值对的映射 package main import ( "encoding/json" "fmt" ) func main() { t1 := make(map[string]interface{}) t1["company"] = "Lianshi" t1["subjects "] = []string{"Go", "Web", "Python", "C++"} t1["isok"] = true t1["price"] = 8980.88 b, err := json.Marshal(t1) if err != nil { fmt.Println("json err:", err) } fmt.Println(string(b)) } $ go run project.go {"company":"Lianshi","isok":true,"price":8980.88,"subjects ":["Go","Web","Python","C++"]}
- json.MarshalIndent()格式化输出
package main import ( "encoding/json" "fmt" ) func main() { //创建字典保存键值对信息 m := make(map[string]interface{}) //数据存储 m["nane"] = "望合" m["age"] = 18 m["score"] = []int{100, 100, 20} m["addr"] = "柬埔寨" //格式化转成json slice, err := json.MarshalIndent(m, "", " ") if err != nil { fmt.Println("json格式转换失败") } else { fmt.Println(string(slice)) } }
3.JSON解码
使用json.Unmarshal()函数将JSON格式的文本解码为Go里面预期的数据结构。
- json.Unmarshal()函数的原型如下:
func Unmarshal(data []byte, v interface{}) error该函数的第一个参数是输入,即JSON格式的文本(比特序列),第二个参数表示目标输出容器,用于存放解码后的值。
3.1解析JSON到结构体
- 定义json结构体
package main import ( "encoding/json" "fmt" ) type Student struct { Name string `json:"name"` Age int `json:"age"` Score []int `json:"score` Addr string `json:"addr"` } func main() { slice := []byte(` { "Addr": "Chain", "Age": 33, "Name": "OPS", "Score": [ 100, 100, 59 ] } `) var stu Student err := json.Unmarshal(slice, &stu) if err != nil { fmt.Println("json格式转换失败") } else { fmt.Println(stu) } }
- json结构体中的字段必须和数据结构体一致
package main import ( "encoding/json" "fmt" ) type language struct { Company string `json:"company` Subjects []string `json:"subjects"` Isok bool `json:"isok"` Price float64 `json:"price"` } func main() { b := []byte(`{ "company": "AliCloud", "subjects": [ "Go", "Web", "Python", "C++" ], "isok": true, "price": 8980.88 }`) var la language err := json.Unmarshal(b, &la) if err != nil { fmt.Println("Failed") } else { fmt.Println(la) } } $ go run project.go {AliCloud [Go Web Python C++] true 8980.88}知道JSON结构的前提下,可以定义一个或者几个适当的结构体进行反序列化
type Human struct { name string `json:"name"` // 姓名 Gender string `json:"s"` // 性别,性别的tag表明在JSON中为s字段 Age int `json:"Age"` // 年龄 Lesson } type Lesson struct { Lessons []string `json:"lessons"` } func main() { jsonStr := `{"Age": 18,"name": "Jim" ,"s": "男", "lessons":["English","History"],"Room":201,"n":null,"b":false}` var hu Human if err := json.Unmarshal([]byte(jsonStr), &hu); err == nil { fmt.Println("\n结构体Human") fmt.Println(hu) } var le Lesson if err := json.Unmarshal([]byte(jsonStr), &le); err == nil { fmt.Println("\n结构体Lesson") fmt.Println(le) } jsonStr = `["English","History"]` var str []string if err := json.Unmarshal([]byte(jsonStr), &str); err == nil { fmt.Println("\n字符串数组") fmt.Println(str) } else { fmt.Println(err) } }
3.2解析任意JSON到map
encoding/json包使用map[string]interface{}和[]interface{}存储任意的JSON对象和数组,它可以被反序列化为任何JSON blob存储到接口值中
直接使用Unmarshal把这个数据反序列化,并保存在map[string]interface{}中。要访问数据,可以使用类型断言
示例map[string]interface{}
func main() { jsonStr := `{"Age": 18,"name": "Jim" ,"s": "男","Lessons":["English","History"],"Room":201,"n":null,"b":false}` var data map[string]interface{} if err := json.Unmarshal([]byte(jsonStr), &data); err == nil { fmt.Println("map结构") fmt.Println(data) } for k, v := range data { switch vv := v.(type) { case string: fmt.Println(k, "是string", vv) case bool: fmt.Println(k, "是bool", vv) case float64: fmt.Println(k, "是float64", vv) case nil: fmt.Println(k, "是nil", vv) case []interface{}: fmt.Println(k, "是array:") for i, u := range vv { fmt.Println(i, u) } default: fmt.Println(k, "未知数据类型") } } }示例[]interface{}
- 定义字典类型json解析map
i.(map[string]interface{})package main import ( "encoding/json" "fmt" ) func main() { //两种方式定义切片,字符串转slice,""中需要\进行转义 //slice:=[]byte("{\"Addr\":\"黑龙江大庆\",\"Age\":33,\"Name\":\"重言\",\"Score\":[100,100,59]}") slice := []byte(`{"Addr":"黑龙江大庆","Age":33.3,"Name":"重言","Score":[100,100,59]}`) var i interface{} err := json.Unmarshal(slice, &i) if err != nil { fmt.Println("json格式转换失败") } //定义字典类型 m := i.(map[string]interface{}) //遍历字典 for k, v := range m { fmt.Println(k, ":", v) } /* $ go run project.go Addr : 黑龙江大庆 Age : 33.3 Name : 重言 Score : [100 100 59] */ for _, t := range m { switch val := t.(type) { case string: fmt.Println("string", val) case float64: fmt.Println("float64", val) case []interface{}: fmt.Println("[]int", val) for i, value := range val { fmt.Printf("[%v]=%v\n", i, value) fmt.Printf("%T\n", value) } } } } $ go run project.go string 黑龙江大庆 float64 33.3 string 重言 []int [100 100 59] [0]=100 float64 [1]=100 float64 [2]=59 float64
4.类型断言
- 使用断言判断类型
m := t.(map[string]interface{})
for k, v := range m {
switch val := v.(type) {package main import ( "encoding/json" "fmt" ) func main() { b := []byte(`{ "company": "Lianshi", "subjects": [ "Go", "Web", "Python", "C++" ], "isok": true, "price": 8980.88 }`) //默认返回值类型为interface类型 以map类型进行格式存储 //可以理解为:json的key为map的key json的value为map的value //格式:map[string]interface{} var t interface{} err := json.Unmarshal(b, &t) if err != nil { fmt.Println("json err:", err) } fmt.Println(t) //使用断言判断类型 m := t.(map[string]interface{}) for k, v := range m { switch val := v.(type) { case string: fmt.Println(k, "is string", val) case int: fmt.Println(k, "is int", val) case float64: fmt.Println(k, "is float64", val) case bool: fmt.Println(k, "is bool", val) case []interface{}: fmt.Println(k, "is an array:") for i, u := range val { fmt.Println(i, u) } default: fmt.Println(k, "is of a type I don't know how to handle") } } }
5.json.Decoder流式解码器
json.Decoder流式解码器,可以利用它依次从字节流里面解码出多个JSON实体
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { resp.Body.Close() return nil, err }
6.JSON数据编码和解码
在需要把JSON格式的数据写入文件时,可以使用json.NewEncoder(或实现io.Writer的类型)得到Encoder,然后通过Encoder的Encode()来进行写入;反过来可以使用json.Decoder和Decode()函数
示例,使用Decode方法解码一段JSON格式数据,同时使用Encode方法将结构体数据保存到文件t.json中
type Human struct { name string `json:"name"` // 姓名 Gender string `json:"s"` // 性别,性别的tag表明在JSON中为s字段 Age int `json:"Age"` // 年龄 Lesson } type Lesson struct { Lessons []string `json:"lessons"` } func main() { // JSON数据的字符串 jsonStr := `{"Age": 18,"name": "Jim" ,"s": "男", "lessons":["English","History"],"Room":201,"n":null,"b":false}` strR := strings.NewReader(jsonStr) h := &Human{} // Decode 解码JSON数据到结构体Human中 err := json.NewDecoder(strR).Decode(h) if err != nil { fmt.Println(err) } fmt.Println(h) // 定义Encode需要的Writer f, err := os.Create("./t.json") // 把保存数据的Human结构体对象编码为JSON保存到文件 json.NewEncoder(f).Encode(h) }上面代码中调用json.NewDecoder函数构造Decoder对象,使用这个对象的Decode方法将JSON串解码给定义好的结构体对象h。对于字符串,使用strings.NewReader方法,让字符串变成一个Reader对象
类似解码过程,通过json.NewEncoder()函数可构造Encoder对象,由于os包中的文件操作已经实现Writer接口,可以把h结构体对象编码为JSON数据格式保存在文件t.sjon中
7.JSON数据延迟解析
对于Human.Name字段,由于可以在使用的时候根据具体数据类型来解析,因此可以延迟解析。当结构体Human的Name字段的类型设置为json.RawMessage时,它将在编码后继续以字节数组方式存在。
type Human struct { Name json.RawMessage `json:"name"` // 姓名,json.RawMessage 类型不会进行解码 Gender string `json:"s"` // 性别,性别的tag表明在JSON中为s字段 Age int `json:"Age"` // 年龄 Lesson } type Lesson struct { Lessons []string `json:"lessons"` } func main() { jsonStr := `{"Age": 18,"name": "Jim" ,"s": "男", "lessons":["English","History"],"Room":201,"n":null,"b":false}` var hu Human if err := json.Unmarshal([]byte(jsonStr), &hu); err == nil { fmt.Printf("\n 结构体Human \n") fmt.Printf("%+v \n", hu) // 可以看到Name字段未解码,还是字节数组 } // 对延迟解码的Human.Name进行反序列化 var UName string if err := json.Unmarshal(hu.Name, &UName); err == nil { fmt.Printf("\n Human.Name: %s \n", UName) } }程序输出
结构体Human {Name:[34 74 105 109 34] Gender:男 Age:18 Lesson:{Lessons:[English History]}} Human.Name: Jim在对JSON数据第一次解码后,依旧是二进制数组,在后面的解码后才真正反序列化为string类型的真是字符串对象