一、官方库编码解码
type Movie struct {
Title string
Year int `json:"released"`
Color bool `json:"color,omitempty"`
Actors []string
}
var movies = []Movie{
{Title: "Casablanca", Year: 1942, Color: false,
Actors: []string{"Humphrey Bogart", "Ingrid Bergman"}},
{Title: "Cool Hand Luke", Year: 1967, Color: true,
Actors: []string{"Paul Newman"}},
{Title: "Bullitt", Year: 1968, Color: true,
Actors: []string{"Steve McQueen", "Jacqueline Bisset"}},
// ...
}
1.编码
将一个Go语言中类似movies的结构体slice转为JSON的过程叫编组(marshaling)。编组通过调用json.Marshal函数完成:
data, err := json.Marshal(movies)
if err != nil {
log.Fatalf("JSON marshaling failed: %s", err)
}
fmt.Printf("%s\n", data)
Marshal函数返还一个编码后的字节slice,包含很长的字符串,并且没有空白缩进;我们将它折行以便于显示:
[{"Title":"Casablanca","released":1942,"Actors":["Humphrey Bogart","Ingr
id Bergman"]},{"Title":"Cool Hand Luke","released":1967,"color":true,"Ac
tors":["Paul Newman"]},{"Title":"Bullitt","released":1968,"color":true,"
Actors":["Steve McQueen","Jacqueline Bisset"]}]
这种紧凑的表示形式虽然包含了全部的信息,但是很难阅读。为了生成便于阅读的格式,另一个json.MarshalIndent函数将产生整齐缩进的输出。该函数有两个额外的字符串参数用于表示每一行输出的前缀和每一个层级的缩进:
data, err := json.MarshalIndent(movies, "", " ")
if err != nil {
log.Fatalf("JSON marshaling failed: %s", err)
}
fmt.Printf("%s\n", data)
细心的读者可能已经注意到,其中Year名字的成员在编码后变成了released,还有Color成员编码后变成了小写字母开头的color。这是因为构体成员Tag所导致的。一个构体成员Tag是和在编译阶段关联到该成员的元信息字符串:
Year int `json:"released"`
Color bool `json:"color,omitempty"`
结构体的成员Tag可以是任意的字符串面值,但是通常是一系列用空格分隔的key:"value"键值对序列;因为值中含义双引号字符,因此成员Tag一般用原生字符串面值的形式书写。json开头键名对应的值用于控制encoding/json包的编码和解码的行为,并且encoding/...下面其它的包也遵循这个约定。成员Tag中json对应值的第一部分用于指定JSON对象的名字,比如将Go语言中的TotalCount成员对应到JSON中total_count对象。Color成员的Tag还带了一个额外的omitempty选项,表示当Go语言结构体成员为空或零值时不生成JSON对象(这里false为零值)。果然,Casablanca是一个黑白电影,并没有输出Color成员。
注:json:"-"表示不进行序列化
2.解码
编码的逆操作是解码,对应将JSON数据解码为Go语言的数据结构,Go语言中一般叫unmarshaling,通过json.Unmarshal函数完成。下面的代码将JSON格式的电影数据解码为一个结构体slice,结构体中只有Title成员。通过定义合适的Go语言数据结构,我们可以选择性地解码JSON中感兴趣的成员。当Unmarshal函数调用返回,slice将被只含有Title信息值填充,其它JSON成员将被忽略。
var titles []struct{ Title string }
if err := json.Unmarshal(data, &titles); err != nil {
log.Fatalf("JSON unmarshaling failed: %s", err)
}
fmt.Println(titles) // "[{Casablanca} {Cool Hand Luke} {Bullitt}]"
也可以使用基于流式的解码器json.Decoder
// NewDecoder 返回从 r 读取的解码器
//
// 解码器自己会进行缓冲,而且可能会从 r 读比解码 JSON 值
// 所需的更多的数据
func NewDecoder(r io.Reader) *Decoder
// Decode 从自己的输入里读取下一个编码好的 JSON 值,
// 并存入 v 所指向的值里
//
// 要知道从 JSON 转换为 Go 的值的细节,
// 请查看 Unmarshal 的文档
func (dec *Decoder) Decode(v interface{}) error
NewDecoder 函数接受一个实现了 io.Reader 接口类型的值作为参数
// This sample program demonstrates how to decode a JSON response
// using the json package and NewDecoder function.
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
)
type (
// gResult maps to the result document received from the search.
gResult struct {
GsearchResultClass string `json:"GsearchResultClass"`
UnescapedURL string `json:"unescapedUrl"`
URL string `json:"url"`
VisibleURL string `json:"visibleUrl"`
CacheURL string `json:"cacheUrl"`
Title string `json:"title"`
TitleNoFormatting string `json:"titleNoFormatting"`
Content string `json:"content"`
}
// gResponse contains the top level document.
gResponse struct {
ResponseData struct {
Results []gResult `json:"results"`
} `json:"responseData"`
}
)
func main() {
uri := "http://ajax.googleapis.com/ajax/services/search/web?v=1.0&rsz=8&q=golang"
// Issue the search against Google.
resp, err := http.Get(uri)
if err != nil {
log.Println("ERROR:", err)
return
}
defer resp.Body.Close()
// Decode the JSON response into our struct type.
var gr gResponse
err = json.NewDecoder(resp.Body).Decode(&gr)
if err != nil {
log.Println("ERROR:", err)
return
}
fmt.Println(gr)
// Marshal the struct type into a pretty print
// version of the JSON document.
pretty, err := json.MarshalIndent(gr, "", " ")
if err != nil {
log.Println("ERROR:", err)
return
}
fmt.Println(string(pretty))
}
二、第三方库
Go 语言里面原生支持了这种数据格式的序列化以及反序列化,内部使用反射机制实现,性能有点差,在高度依赖 json 解析的应用里,往往会成为性能瓶颈,好在已有很多第三方库帮我们解决了这个问题
参考
golang json 性能分析
Golang的json包一览
突然发现一个很好用Golang的json库---gjson
从上面的结果可以看出来:
easyjson 无论是序列化还是反序列化都是最优的,序列化提升了1倍,反序列化提升了3倍
jsoniter 性能也很好,接近于easyjson,关键是没有预编译过程,100%兼容原生库
ffjson 的序列化提升并不明显,反序列化提升了1倍
codecjson 和原生库相比,差不太多,甚至更差
jsonparser 不太适合这样的场景,性能提升并不明显,而且没有反序列化
所以综合考虑,建议大家使用 jsoniter,如果追求极致的性能,考虑 easyjson
网友评论