golang中json与struct中tag

使用

Golang 的 encoding/json 库已经提供了很好的封装,可以让我们很方便地进行 JSON 数据的转换。

Go 语言中数据结构和 JSON 类型的对应关系如下表:

golang 类型 JSON 类型 注意事项
bool JSON booleans  
浮点数、整数 JSON numbers  
字符串 JSON strings 字符串会转换成 UTF-8 进行输出,无法转换的会打印对应的 unicode 值。而且为了防止浏览器把 json 输出当做 html, “<”、”>” 以及 “&” 会被转义为 “\u003c”、”\u003e” 和 “\u0026”。
array,slice JSON arrays []byte 会被转换为 base64 字符串,nil slice 会被转换为 JSON null
struct JSON objects 只有导出的字段(以大写字母开头)才会在输出中

NOTE:Go 语言中一些特殊的类型,比如 Channel、complex、function 是不能被解析成 JSON 的。

Encode 和 Decode

要把 golang 的数据结构转换成 JSON 字符串(encode),可以使用 Marshal函数:

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

比如我们有结构体 User

type User struct {
    Name string
    IsAdmin bool
    Followers uint
}

以及一个实例:

user := User{
		Name:      "cizixs",
		IsAdmin:   true,
		Followers: 36,
	}
data, err := json.Marshal(user)

那么 data 就是 []byte 类型的数组,里面包含了解析为 JSON 之后的数据:

data == []byte(`{"Name":"cizixs","IsAdmin":true,"Followers":36}`)

相对应的,要把 JSON 数据转换成 Go 类型的值(Decode), 可以使用 json.Unmarshal。它的定义是这样的:

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

data 中存放的是 JSON 值,v 会存放解析后的数据,所以必须是指针,可以保证函数中做的修改能保存下来。

下面看个例子:

data = []byte(`{"Name":"gopher","IsAdmin":false,"Followers":8900}`)
var newUser = new(User)
err = json.Unmarshal(data, &newUser)
if err != nil {
	fmt.Errorf("Can not decode data: %v\n", err)
}
fmt.Printf("%v\n", newUser)

那么 Unmarshal 是怎么找到结构体中对应的值呢?比如给定一个 JSON key Filed,它是这样查找的:

  • 首先查找 tag 名字(关于 JSON tag 的解释参看下一节)为 Field 的字段
  • 然后查找名字为 Field 的字段
  • 最后再找名字为 FiElD 等大小写不敏感的匹配字段。
  • 如果都没有找到,就直接忽略这个 key,也不会报错。这对于要从众多数据中只选择部分来使用非常方便。

    更多控制:Tag

    在定义 struct 字段的时候,可以在字段后面添加 tag,来控制 encode/decode 的过程:是否要 decode/encode 某个字段,JSON 中的字段名称是什么。

    可以选择的控制字段有三种:

  • -:不要解析这个字段
  • omitempty:当字段为空(默认值)时,不要解析这个字段。比如 false、0、nil、长度为 0 的 array,map,slice,string
  • FieldName:当解析 json 的时候,使用这个名字
    举例来说吧:
    // 解析的时候忽略该字段。默认情况下会解析这个字段,因为它是大写字母开头的
    
    Field int   `json:"-"` 
    
    
    // 解析(encode/decode) 的时候,使用
    
    `other_name`,而不是 `Field`Field int   `json:"other_name"` 
    
    
    // 解析的时候使用 `other_name`,如果struct 中这个值为空,就忽略它
    
    Field int   `json:"other_name,omitempty"`

    由于原文篇幅较长,这里只转载一部分,可去原文查看全部内容http://cizixs.com/2016/12/19/golang-json-guide

    自己写几个小demo测试一下:

    package main
     
    import (
    	"encoding/json"
    	"fmt"
    )
     
    type peerInfo struct {
    	HTTPPort int
    	TCPPort  int
    	versiong string
    }
     
    func main() {
    	pi := peerInfo{80, 3306, "0.0.1"}
    	js, err := json.Marshal(pi)
    	if err != nil {
    		fmt.Println(err)
    	}
    	fmt.Println(string(js))
    }
    

    输出结果:

    {"HTTPPort":80,"TCPPort":3306}
     

    首字母为小写时,为private字段,不会转换

    struct中添加tag:

    package main
     
    import (
    	"encoding/json"
    	"fmt"
    )
     
    type peerInfo struct {
    	HTTPPort int `json:"http_port"`
    	TCPPort  int `json:"tcp_port"`
    	versiong string
    }
     
    func main() {
    	pi := peerInfo{80, 3306, "0.0.1"}
    	js, err := json.Marshal(pi)
    	if err != nil {
    		fmt.Println(err)
    	}
    	fmt.Println(string(js))
    }
    

    输出结果:

    {"http_port":80,"tcp_port":3306}

    json转换成go对象:

    package main
     
    import (
    	"encoding/json"
    	"fmt"
    )
     
    type peerInfo struct {
    	HTTPPort int `json:"http_port"`
    	TCPPort  int `json:"tcp_port"`
    	versiong string
    }
     
    func main() {
    	var v peerInfo
    	data := []byte(`{"http_port":80,"tcp_port":3306}`)
    	err := json.Unmarshal(data, &v)
    	if err != nil {
    		fmt.Println(err)
    	}
    	fmt.Printf("%+v\n", v)
    }
    

    输出结果:

    {HTTPPort:80 TCPPort:3306 versiong:}

    去除tag:

    package main
     
    import (
    	"encoding/json"
    	"fmt"
    )
     
    type peerInfo struct {
    	HTTPPort int
    	TCPPort  int
    	versiong string
    }
     
    func main() {
    	var v peerInfo
    	data := []byte(`{"http_port":80,"tcp_port":3306}`)
    	err := json.Unmarshal(data, &v)
    	if err != nil {
    		fmt.Println(err)
    	}
    	fmt.Printf("%+v\n", v)
    }
    

    得到结果:

    {HTTPPort:0 TCPPort:0 versiong:}

    无法解析到对象中。

    最后,再贴一下原文中关于json.Unmarshal的几点:

  • 首先查找 tag 名字(关于 JSON tag 的解释参看下一节)为 Field 的字段
  • 然后查找名字为 Field 的字段
  • 最后再找名字为 FiElD 等大小写不敏感的匹配字段。
  • 如果都没有找到,就直接忽略这个 key,也不会报错。

猜你喜欢

转载自blog.csdn.net/chenyulancn/article/details/82929803