JSON
JSON(JavaScript Object Notation)是一种基于文本描述的轻量级数据交换语言。
它的优点包括自我描述,易于理解等。即使它是JavaScript的一个子集,JSON使用不同的文本格式,结果是它可以被视为一种独立的语言。
JSON与C系列语言具有相似性。
JSON和XML之间最大的区别在于XML是一种完整的标记语言,而JSON则不是。
JSON比XML更小更快,因此在浏览器中解析更加容易和快捷,这也是许多开放平台选择使用JSON作为数据交换接口语言的原因之一。
由于JSON在Web开发中变得越来越重要,让我们来看看Go对JSON的支持程度。
你会发现Go的标准库对编码和解码JSON有很好的支持。
{"servers":[{"serverName":"Shanghai_VPN","serverIP":"127.0.0.1"},{"serverName":"Beijing_VPN","serverIP":"127.0.0.2"}]}
后面的部分将使用此JSON数据在Go中引入JSON概念。
解析JSON
解析为Struct
假设我们在上面的例子中有JSON。
我们如何解析这些数据并将其映射到Go中的结构?Go为此提供了以下功能:
func Unmarshal(data []byte, v interface{}) error
我们可以像这样使用这个函数:
package controllers
import (
"encoding/json"
"fmt"
"net/http"
)
type JsonServer struct {
ServerName string
StringIP string
}
type JsonServers struct {
Servers []JsonServer
}
func Json(w http.ResponseWriter, r *http.Request) {
var s JsonServers
str := `{"servers":[{"serverName":"Shanghai_VPN","serverIP":"127.0.0.1"},{"serverName":"Beijing_VPN","serverIP":"127.0.0.2"}]}`
json.Unmarshal([]byte(str), &s)
fmt.Println(str)
}
运行后得到如下结果
{[{Shanghai_VPN 127.0.0.1} {Beijing_VPN 127.0.0.2}]}
在上面的示例中,我们在Go中为JSON定义了相应的结构,使用slice作为JSON对象数组,使用字段名作为JSON键。
但Go如何知道哪个JSON对象对应于哪个特定结构域?
假设我们在JSON中有一个名为Foo的键。
我们如何找到相应的字段?
- 首先,Go尝试查找标签包含Foo的(大写)导出字段。
- 如果找不到匹配项,请查找名称为Foo的字段。
- 如果仍然没有匹配,请查找类似FOO或FoO的内容,忽略区分大小写。
您可能已经注意到,应该导出要分配的所有字段,并且Go仅分配可以找到的字段,而忽略所有其他字段。
如果您需要处理大块的JSON数据,但只有它的一个特定子集,这可能很有用。
您不需要的数据很容易被丢弃。
解析为接口
当我们事先知道期望什么样的JSON时,我们可以将它解析为特定的结构。
但是,如果我们不知道怎么办?
我们知道interface{}可以是Go中的任何内容,因此它是保存未知格式的JSON的最佳容器。
JSON包使用map[string]interface{}和[]interface{}来保存各种JSON对象和数组。
以下是JSON映射关系列表:
- bool代表JSON布尔值,
- float64代表JSON数字,
- string表示JSON字符串,
- nil表示JSON null。
假设我们有以下JSON数据:
b := []byte(`{"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]}`)
现在我们将这个JSON解析为interface{}:
var f interface{}
err := json.Unmarshal(b, &f)
f存储一个映射,其中键是字符串,值是interface{}'s'。
f = map[string]interface{}{
"Name": "Wednesday",
"Age": 6,
"Parents": []interface{}{
"Gomez",
"Morticia",
},
}
那么,我们如何访问这些数据?键入断言。
m := f.(map[string]interface{})
断言后,您可以使用以下代码访问数据:
for k, v := range m {
switch vv := v.(type) {
case string:
fmt.Println(k, "is string", vv)
case int:
fmt.Println(k, "is int", vv)
case float64:
fmt.Println(k,"is float64",vv)
case []interface{}:
fmt.Println(k, "is an array:")
for i, u := range vv {
fmt.Println(i, u)
}
default:
fmt.Println(k, "is of a type I don't know how to handle")
}
}
如您所见,我们现在可以通过interface{}解析未知格式的JSON并键入断言。
以上示例是官方解决方案,但类型断言并不总是方便。
因此,我推荐一个名为simplejson的开源项目,由bitly创建和维护。
以下是如何使用此项目处理未知格式的JSON的示例:
js, err := NewJson([]byte(`{
"test": {
"array": [1, "2", 3],
"int": 10,
"float": 5.150,
"bignum": 9223372036854775807,
"string": "simplejson",
"bool": true
}
}`))
arr, _ := js.Get("test").Get("array").Array()
i, _ := js.Get("test").Get("int").Int()
ms := js.Get("test").Get("string").MustString()
不难看出这是多么方便。
查看存储库以查看更多信息:
https://github.com/bitly/go-s...
生成JSON
在许多情况下,我们需要生成JSON数据并响应客户端。
在Go中,JSON包有一个名为Marshal的函数来执行以下操作:
func Marshal(v interface{}) ([]byte, error)
假设我们需要生成服务器信息列表。
我们有以下样本:
package controllers
import (
"encoding/json"
"fmt"
"net/http"
)
type JsonServer struct {
ServerName string
ServerIP string
}
type JsonServers struct {
Servers []JsonServer
}
func JsonProcess(w http.ResponseWriter, r *http.Request) {
var s JsonServers
s.Servers = append(s.Servers, JsonServer{
ServerName: "Shanghai_VPN",
ServerIP: "127.0.0.1",
})
s.Servers = append(s.Servers, JsonServer{
ServerName: "Beijing_VPN",
ServerIP: "127.0.0.2",
})
b, err := json.Marshal(s)
if err != nil {
fmt.Println("json err:", err)
return
}
fmt.Println(string(b))
}
输出结果如下:
{"Servers":[{"ServerName":"Shanghai_VPN","ServerIP":"127.0.0.1"},{"ServerName":"Beijing_VPN","ServerIP":"127.0.0.2"}]}
如您所知,所有字段名称都是大写的,但如果您希望JSON键名以小写字母开头,则应使用struct标签。
否则,Go将不会为内部字段生成数据。
type JsonServer struct {
ServerName string `json:"serverName"`
ServerIP string `json:"serverIP"`
}
type JsonServers struct {
Servers []JsonServer `json:"servers"`
}
在此修改之后,我们可以生成与以前相同的JSON数据。
在尝试生成JSON时,您需要记住以下几点:
- 不输出包含“ - ”的字段标签。
- 如果标记包含自定义名称,则Go使用此名称而不是字段名称,如上例中的serverName。
- 如果标签包含omitempty,则如果该字段为零值,则不会输出该字段。
- 如果字段类型是bool,string,int,int64等,并且其标记包含",string",则Go会将此字段转换为其对应的JSON类型。
示例如下
type TestJsonServer struct {
// ID不会被输出
ID int `json:"-"`
// ServerName2 不会转换为JSON类型
ServerName string `json:"serverName"`
ServerName2 string `json:"serverName2,string"`
// 如果ServerIP是空的将不会被输出
ServerIP string `json:"serverIP,omitempty"`
}
func JsonToTest(w http.ResponseWriter, r *http.Request) {
s := TestJsonServer{
ID: 3,
ServerName: `Go "1.0" `,
ServerName2: `Go "1.0" `,
ServerIP: ``,
}
b, _ := json.Marshal(s)
os.Stdout.Write(b)
}
输出如下
{"serverName":"Go \"1.0\" ","serverName2":"\"Go \\\"1.0\\\" \""}
Marshal函数只有在成功时返回数据,所以这里有一些我们需要记住的要点:
- JSON仅支持字符串作为键,因此如果要对映射进行编码,则其类型必须为map[string]T,其中T是Go中的类型。
- 通道,复杂类型和函数等类型无法编码为JSON。
- 不要尝试编码循环数据,它会导致无限递归。
- 如果该字段是指针,则Go输出它指向的数据,否则如果指向nil则输出null。
有疑问加站长微信联系(非本文作者)