【从零开始捡起GO】14. 结构体和json

json

json格式可以算我们日常最常用的序列化格式之一了,在开发中,经常需要将json和结构体互相转换。go作为一个强类型语言,在转换上需要注意一些事项和设置,今天来总结一下常见的json和结构体之间互相转换的要点。

json转化的相关包和方法

go语言为json转化提供了一个json包:

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

使用json.Marshal将一个数据转化为json

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

使用json.Unmarshal将一串json还原为某种数据

直接转换会出现的问题

首先,在测试中提供了一个第三方包:Person

type Person struct {
	Name    string
	Age     int
	Married bool
	money   int
}

func (p *Person) SetMoney(m int) {
	p.money = m
}

func (p Person) ToString() {
	fmt.Printf("name:%#v, age:%#v married:%#v, money:%#v\r\n", p.Name, p.Age, p.Married, p.money)
}

Person这个结构体拥有4个成员:

  1. 对外开放的 Name
  2. 对外开放的 Age
  3. 对外开放的 Married
  4. 不对外开放的 money

这里直接使用 json.Marshal 会发生什么呢?

func main() {
	p := &person.Person{Name: "张三", Age: 12, Married: false}
	p.SetMoney(99)
	p.ToString()
	// name:"张三", age:12 married:false, money:99
	j, _ := json.Marshal(p)
	// {"Name":"张三","Age":12,"Married":false}
	fmt.Println(string(j))
}

转换后发现,不对外开放的 money 不见了。这是因为 json 包对 person 包来说是一个外部包,它是无法访问到不对外开放的 money 的。这里有两种解决方法:

改变成员权限

我们可以将 money 成员改为对外开放访问的 Money,但是这样会破坏结构体的封装性,不推荐使用。

在包内进行json转化

在包内创建一个转json的方法,创建一个临时的,可对外访问的相同结构的结构体:

func (p Person) Marshal() ([]byte, error) {
	type JsonPerson struct {
		Name    string
		Age     int
		Married bool
		Money   int
	}
	jp := &JsonPerson{Name: p.Name, Age: p.Age, Married: p.Married, Money: p.money}
	js, err := json.Marshal(jp)
	return js, err
}

在外部调用 p.marshal()

func main() {
	p := &person.Person{Name: "张三", Age: 12, Married: false}
	p.SetMoney(99)
	p.ToString()
	// name:"张三", age:12 married:false, money:99
	j, _ := p.Marshal()
	fmt.Println(string(j))
	// {"Name":"张三","Age":12,"Married":false,"Money":99}
}

同样的,一个包含非对外开放成员的json字符串是无法初始化非对外开放的成员的,在内部定义一个 Unmarshal 方法

func (p *Person) Unmarshal(js []byte) {
	type JsonPerson struct {
		Name    string
		Age     int
		Married bool
		Money   int
	}
	jp := &JsonPerson{}
	err := json.Unmarshal(js, jp)
	*p = Person{Name: jp.Name, Age: jp.Age, Married: jp.Married, money: jp.Money}
	if err != nil {
		fmt.Println(err)
	}
}

调用 Unmarshal() 方法

func main() {
	p := &person.Person{Name: "张三", Age: 12, Married: false}
	p.SetMoney(99)
	p.ToString()
	// name:"张三", age:12 married:false, money:99
	j, _ := p.Marshal()
	fmt.Println(string(j))
	// {"Name":"张三","Age":12,"Married":false,"Money":99}
	p1 := &person.Person{}
	p1.Unmarshal(j)
	p1.ToString()
	// name:"张三", age:12 married:false, money:99
}

Tag

虽然已经可以实现json和结构体互相转化了,但是上面的例子还是有问题的。通常在编写api接口给其他语言或者前端的时候,首字母是要求小写的。这里返回值都是大写首字母,应该怎么办呢?这个时候就要用到成员的 tag

一个标签 tag 需要用 ``包裹起来,这里介绍3个标签:json-omitempty

json

json标签是用来在进行json互转的时候起的别名,举个例子:

type Person struct {
	Name    string `json:"name"`
	Age     int    `json:"age"`
	Married bool   `json:"married"`
	money   int
}
func main() {
	p := person.Person{Name: "李四", Age: 11, Married: false}
	js,_ := json.Marshal(p)
	fmt.Println(string(js))
	// {"name":"李四","age":11,"married":false}
	p1 := &person.Person{}
	_ = json.Unmarshal(js,p1)
	fmt.Println(p1)
	// &{李四 11 false 0}
}

可以看到,在转json的时候,互相转化时成员转化为了标签内的内容。

-

-标签用来跳过某个成员:

type Person struct {
	Name    string `json:"-"`
	Age     int    `json:"age"`
	Married bool   `json:"married"`
	money   int
}
func main() {
	p := person.Person{Name: "李四", Age: 11, Married: false}
	js,_ := json.Marshal(p)
	fmt.Println(string(js))
	// {"age":11,"married":false}
	p1 := &person.Person{}
	_ = json.Unmarshal(js,p1)
	fmt.Println(p1)
	// &{ 11 false 0}
}
omitempty

omitempty 用来跳过一个字段的空值

type Person struct {
	Name    string `json:"-"`
	Age     int    `json:"age,omitempty"`
	Married bool   `json:"married"`
	money   int
}
func main() {
	p := person.Person{Name: "李四", Age: 0, Married: false}
	js,_ := json.Marshal(p)
	fmt.Println(string(js))
	// {"married":false}
	p1 := &person.Person{}
	_ = json.Unmarshal(js,p1)
	fmt.Println(p1)
	// &{ 0 false 0}
}

当你不需要起别名时,应该使用,omitempty而不是omitempty:

type Person struct {
	Name    string `json:"omitempty"`
	Age     int    `json:",omitempty"`
	Married bool   `json:"married"`
	money   int
}
func main() {
	p := person.Person{Name: "", Age: 0, Married: false}
	js,_ := json.Marshal(p)
	fmt.Println(string(js))
	// {"omitempty":"","married":false}
	p1 := &person.Person{}
	_ = json.Unmarshal(js,p1)
	fmt.Println(p1)
	// &{ 0 false 0}
}

小总结

入门思维导图下载(使用xmind打开):golang.xmind

程序幼儿员-龚学鹏
请先登录后发表评论
  • latest comments
  • 总共0条评论