go中json的使用

go中json的使用

go 原生提供了json的加码解码支持,import "encoding/json"就可以了。

1. encode 编码

json的 encode 加码方法是:

func Marshal(v interface{}) ([]byte, error)

正是因为参数是接口 interfae{},所以,理论上可以传入任何数据。

1.1 struct 编码

我们一般都用用结构体作为数据源,给他json加码操作,因为它的类型可以加入任意的数据类型,符合json字符串的特性。

我们先定义2个结构体,作为数据类型。

//1. 先定义1个结构体
type Animal struct {
	Name  string
	Order string
}

// 定义结构体嵌套
type List struct {
	Ret  int
	Info []Animal
}

要想对结构体实现json加码操作,得实例化1个变量出来。


//实例化1个变结构体量
var list0 = Animal{Name: "wo", Order: "HELLO"}

//encode
list9, err := json.Marshal(list0)
if err != nil {
	_ = fmt.Errorf("%v", err)
}

//输出的list9是一个字符的切片 []byte, 得用string() 方法转换成字符串 
fmt.Printf("%v\n", string(list9)) 

输出的json字符串如下:

{"Name":"wo","Order":"HELLO"}

我们来1个复杂的结构体,实现更复杂的json

//  结构体重复嵌套

	//由于需要嵌套,所以得定义1个切片结构体。
	var animals []Animal

	//通过append给切片添加数据。
	animals = append(animals, Animal{Name: "Platypus", Order: "Monotremata"})
	animals = append(animals, Animal{Name: "Quoll", Order: "Dasyuromorphia"})

	// encode
	list2, err := json.Marshal(animals)
	if err != nil {
		_ = fmt.Errorf("%v", err)
	}

	// 转换成字符串
	fmt.Printf("%v\n", string(list2)) //

输出json如下:

[{"Name":"Platypus","Order":"Monotremata"},{"Name":"Quoll","Order":"Dasyuromorphia"}]

我们再来看另外一种嵌套方式,子元素嵌套类型。

// 3. 子元素嵌套类型的结构体

	var animals []Animal
	
	//通过append给切片添加数据。
	animals = append(animals, Animal{Name: "Platypus", Order: "Monotremata"})
	animals = append(animals, Animal{Name: "Quoll", Order: "Dasyuromorphia"})
	
	//初始化
	var list1 = List{Ret: 0, Info: animals}

	list3, err := json.Marshal(list1)
	if err != nil {
		_ = fmt.Errorf("%v", err)
	}
	fmt.Printf("%v\n", string(list3))

输出json如下:

  {"Ret":0,"Info":[{"Name":"Platypus","Order":"Monotremata"},{"Name":"Quoll","Order":"Dasyuromorphia"}]}

我们可以用一个pretty的类库,使得输出结构更加人类可见。

import 	"github.com/tidwall/pretty"

aa := pretty.Pretty(list3)
fmt.Println(string(aa))

输出如下:

{
		  "Ret": 0,
		  "Info": [
		    {
		      "Name": "Platypus",
		      "Order": "Monotremata"
		    },
		    {
		      "Name": "Quoll",
		      "Order": "Dasyuromorphia"
		    }
		  ]
		}

1.2 map 编码

由于map的格式是固定的,没法实现更加丰富的结构,所以一般用map的不多,但是,如果我们对外输出的内容确实是简单固定的,那就可以用map


//定义map类型
personSalary := make(map[string]int)

//开始生成内容
personSalary["steve"] = 12000
personSalary["jamie"] = 15000
personSalary["mike"] = 123

//加码
b, _ := jsoniter.Marshal(personSalary)
fmt.Println(string(pretty.Pretty(b)))

输出如下:


{
  "jamie": 15000,
  "mike": 123,
  "steve": 12000
}

1.3 [] 数组/切片编码

同样,如果我们数据类似都是数组格式的json,那就可以用数组/切片


//切片
a := []int{1,2,3,4}

//数组
//a := [4]string{"hello","world","ni","hao"}

b1, _ := jsoniter.Marshal(a)
fmt.Println(string(pretty.Pretty(b1)))
[1, 2, 3, 4]
["hello", "world", "ni", "hao"]

2. deocode 解码

自带的json类库的decode加码方法是:Unmarshal

func Unmarshal(data []byte, v interface{}) error {}

go里面的解码是比较麻烦的,要想解码json,你得根据这个json字符串里的字段内容,先定义解码出来的结构体,再实例化这个结构体,再调用解码函数。初次使用非常不习惯。

2.1 解码成struct

和加码struct操作步骤完全反过来,去做一遍。

比如,有这样一个json字符串

{"Name":"wo","Order":"HELLO"}

他的数据类型,其实很简单,类型都是一样,其实我们可以解析成map也是OK的,我们先解析成struct吧。看一下,这个json,有2个字段,1个Name,1个Order,所以我们也新建有这2个字段的结构体。


//第一步,我们得定义接受的结构体

type ListOne struct {
	Name  string
	Order string
}

//第二步,实例化这个结构体
var ListInfo1 ListInfo

//第三步,转换成[]byte, 再引用调用,&, 传入指针变量,因为需要修改这个值。
err2 := json.Unmarshal([]byte(jsonStr), &ListInfo1)
	if err2 != nil {
		log.Fatal(err)
	}

fmt.Printf("%T,%+v\n", ListInfoOne3, ListInfoOne3) 

//打印 Name字段
fmt.Printf("%v\n", ListInfoOne3.Name) 

最后得到了这个结构体:

main.ListInfo3,{Name:wo Order:HELLO}

Wo

再继续,复杂一点的json,我们看下如何解析成struct

[
  {
    "Name": "Platypus",
    "Order": "Monotremata"
  }, 
  {
    "Name": "Quoll",
    "Order": "Dasyuromorphia"
  }
]

分析一下,这个json 是一个数组,里面有2个子元素,每1个子元素里面就是1个struct。所以,我们得用一个[]struct这样的数据来接受。

所以,这个结构体长这样:

[]struct
//先定义结构体
	type ListInfo2 struct {
		Name  string
		Order string
	}
	
	//构造1个[]struct 数组/切片
	var ListInfoOne2 []ListInfo2

	//开始解码
	err3 := json.Unmarshal([]byte(jsonStr), &ListInfoOne2)
	if err3 != nil {
		log.Fatal(err)
	}

	fmt.Printf("%T,%+v\n", ListInfoOne2, ListInfoOne2) 

	//打印第0个元素的Name字段的值
	fmt.Printf("%v\n", ListInfoOne2[0].Name) 

打印出来的数据如下:

[]main.ListInfo2,  [{Name:Platypus Order:Monotremata} {Name:Quoll Order:Dasyuromorphia}]

Platypus

再继续,更加复杂的json,我们看下如何解析成struct。

{
		  "Ret": 0,
		  "Info": [
		    {
		      "Name": "Platypus",
		      "Order": "Monotremata"
		    },
		    {
		      "Name": "Quoll",
		      "Order": "Dasyuromorphia"
		    }
		  ]
		}

我们先分析,如何定义结构体,来接受这个json,首先,外面是一个大的struct,其中Info字段是个数组,数组的子元素又是一个struct。OK,有了这个定义之后,我们就可以开始弄了。

所以,这个结构体,大致是长这样的,看样子,得定义2个结构体,搞嵌套

struct {
	Ret: 
	Info : [] struct 
}

开始写:

//先定义结构体

	type ListOne struct {
		Name  string
		Order string
	}

	type ListInfo struct {
		Ret  int
		Info []ListOne
	}

	var ListInfo1 ListInfo
	
	//解码
	err2 := json.Unmarshal([]byte(jsonStr), &ListInfo1)
	if err2 != nil {
		log.Fatal(err)
	}

	fmt.Printf("%+v\n", ListInfo1)
    fmt.Printf("%+v\n", ListInfo1.Info[1].Name) 

输出如下:

{Ret:0 Info:[{Name:Platypus Order:Monotremata} {Name:Quoll Order:Dasyuromorphia}]}

Quoll

2.2 解码成map

解码成map,首先要保证这个json字符串很简单,k和v的数据类型都是重复一样的,就可以,用map来接受,和stuct一样,我们得定义这个map,再去解码

{
    "steve": 12000,
    "jamie": 15000,
    "mike": 123
}

比如,这个json,分析一下,它的k和v都是相同的结构,可以用map

定义的map大致如下

map[string]int
var a1 map[string]int
_ = json.Unmarshal([]byte(jsonStr), &a1)
fmt.Println(a1)
fmt.Println(a1["mike"])

打印如下:

map[jamie:15000 mike:123 steve:12000]
123

再看一下,这个比较复杂的json,

[
  {
    "Name": "Platypus",
    "Order": "Monotremata"
  }, 
  {
    "Name": "Quoll",
    "Order": "Dasyuromorphia"
  }
]

这个json,我们分析一下,可以得到是如下一个map数组

[]map[string]string

代码如下:

var bb1 []map[string]string
_ = json.Unmarshal([]byte(jsonStr5), &bb1)

fmt.Printf("%+v\n", bb1)
fmt.Printf("%+v\n", bb1[0]["Name"])
[map[Name:Platypus Order:Monotremata] map[Name:Quoll Order:Dasyuromorphia]]

Platypus

2.3 解码成数组切片

对于非常简单的数组类型的json,比如:

[1,2,3,4]

那就可以解码成go数组了。

	var bb1 []int
	var jsonStr5 = `[1,2,3]`
	_ = json.Unmarshal([]byte(jsonStr5), &bb1)
	fmt.Printf("%+v\n", bb1)
	fmt.Printf("%+v\n", bb1[0])

输出为:

[1 2 3]
1

3. struct的tag标签

strut 转成json的时候,支持加一个tag的标签。来支持更加丰富的json支持。

你想象一个场景,json的字符串,k的首字母有些都是小写的,可以是在GO里面,小写字母表示私有的,外部不可见的,所以,json 转成 struct后,怎么解决这个问题呢?再比如,json里面有些字段类型是string类型的数字,但是struct里面字段是int型的,怎么匹配呢?

//Golang中可以为结构体的字段添加tag, json:

	/**

	-:不要解析这个字段,表示该字段不会输出到 JSON

	,omitempty 当字段为空(默认值)时,不要解析这个字段。比如 false、0、nil、长度为 0 的 array,map,slice,string,就不会输出到JSON 串中

	FieldName,当解析 json 的时候,使用这个名字

	,string当字段类型是 bool, string, int, int64 等,而 tag 中带有该选项时,那么该字段在输出到 JSON 时,会把该字段对应的值转换成 JSON 字符串.

	*/

我们可以用json关键字,就可以实现刚说的这些问题。

type bbc struct {
		Name   string `json:"-"`          //解析的时候忽略该字段。默认情况下会解析这个字段,因为它是大写字母开头的
		Age    int    `json:"age"`        //别名,解析(encode/decode) 的时候,使用 `age`,而不是 `Age`
		Sex    int    `json:",omitempty"` // 表示如果为空,就不解析了
		Height int    `json:"h,string"`   // 先用h替换,再类型转换,转成string
	}
	
var bb2 = bbc {
		Name:   "yang", // 输出json, 就去掉,忽略了
		Age:    12,     // 变成:age
		Sex:    0,      // 因为是0,所以就忽略了。
		Height: 180,    //输出json 就变成了 h, 字符串类型
	}
	cd, _ := json.Marshal(bb2)
	fmt.Printf("%+v\n", string(cd)) // {"age":12,"Height":"180"}

我们看下输出的是什么呢?达到了我们的目的。

{"age":12,"h":"180"}

同理,如果先得到了1个json,如何按照规则转换成json呢?

比如,有这样一个json:

{
    "age": 12,
    "height": "180",
    "name": "bigbang",
    "sex": ""
}

我们想转换成这样的go struct:

type bbc4 struct {
		Name   string
		Age    int
		Sex    int
		Height int
	}

怎么写这个json:"" 关键字呢?首先是:别名、string转int、空变量就不解析,OK,开始写:

type bbc4 struct {
		Name   string `json:"name"`
		Age    int `json:"age"`
		Sex    string `json:"sex,omitempty"`
		Height int `json:"height,string"`
	}
	
var bb1 bbc4

var jsonStr5 = `{"age":12,"height":"180","name":"bigbang","sex":""}`
_ = json.Unmarshal([]byte(jsonStr5), &bb1)
fmt.Printf("%+v\n", bb1)

看下输出:

{Name:bigbang Age:12 Sex: Height:180}

4. 性能更好的json第三方库

github.com/json-iterator/go 是一个性能更高的第三方json库,它的调用法式和系统自带的json库是一模一样。所以,用起来还是非常方便。

import jsoniter "github.com/json-iterator/go"

//加码
err4 := jsoniter.Unmarshal([]byte(jsonStr3), &ListInfoOne3)
	if err4 != nil {
		fmt.Println(err4)
	}

//解码
rtt, _ := jsoniter.Marshal(ListInfoOne3)

我们可以用benchmark跑一个测试看下,性能

package main

import (
	"encoding/json"
	"github.com/json-iterator/go"
	"github.com/tidwall/gjson"
	"testing"
)

type Animal struct {
	Name  string
	Order string
}

var testString = `{"Name": "Platypus", "Order": "Monotremata"}`

func BenchmarkJsoniter(b *testing.B) {
	var animal Animal

	for i := 0; i < b.N; i++ {
		var err error
		var jsonBlob = []byte(testString)

		err = jsoniter.Unmarshal(jsonBlob, &animal)

		if err != nil {
			//b.Log("error:%v\n", err)
		}
	}
}

func BenchmarkJson(b *testing.B) {
	var animal Animal
	for i := 0; i < b.N; i++ {
		var err error
		var jsonBlob = []byte(testString)

		err = json.Unmarshal(jsonBlob, &animal)
		if err != nil {
			//b.Log("error:%v\n", err)
		}
	}
}

跑个测试看下,发现,居然同样的解码,比自带的性能搞了4倍多。

$  go test -bench=. -run=none json_test.go
goos: darwin
goarch: amd64
BenchmarkJsoniter-12             4199409               289 ns/op
BenchmarkJson-12                 1447922               809 ns/op
PASS
ok      command-line-arguments  4.466s

5. 性能更好,使用更方便的json第三方解码库

前面,我就吐槽过,解码一个json的时候,必须得参照这个json的结构,先定义一个struct或者map来接受这个转换后的数据,非常的麻烦和蛋疼。想象一下,如果一个json里面有几十上百个字段,那就得先定义,非常之蛋疼。更何况,有时候,我们其实只想获取这个json里面的某一个的值。

终于有这样一个第三方类库,可以拯救我们:

package main

import "github.com/tidwall/gjson"

const json = `{"name":{"first":"Janet","last":"Prichard"},"age":47}`

func main() {
	value := gjson.Get(json, "name.last")
	println(value.String())  // Prichard
}

是不是很爽啊。直接获取,不需要预先定义struct数据。

©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页