命令记不住

登陆

JSON 操作

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类型的真是字符串对象

推荐阅读