您的位置:首页 > Web前端 > JavaScript

Golang的json包

2019-11-10 16:32 3691 查看

encoding/json

encoding/json
是官方提供的标准json, 实现RFC 7159中定义的JSON编码和解码。使用的时候需要预定义
struct
,原理是通过
reflection
interface
来完成工作, 性能低。

常用的接口:

  • func Marshal(v interface{}) ([]byte, error)
    生成JSON
  • func Unmarshal(data []byte, v interface{}) error
    解析JSON到
    struct

示例1 生成JSON:

type ColorGroup struct {
ID     int
Name   string
Colors []string
}

group := ColorGroup{
ID:     1,
Name:   "Reds",
Colors: []string{"Crimson", "Red", "Ruby", "Maroon"},
}

b, err := json.Marshal(group)
if err != nil {
fmt.Println("error:", err)
}

os.Stdout.Write(b)

Output:

{"ID":1,"Name":"Reds","Colors":["Crimson","Red","Ruby","Maroon"]}

示例2 解析JSON:

var jsonBlob = []byte(`[
{"Name": "Platypus", "Order": "Monotremata"},
{"Name": "Quoll",    "Order": "Dasyuromorphia"}
]`)

type Animal struct {
Name  string
Order string
}
var animals []Animal
err := json.Unmarshal(jsonBlob, &animals)
if err != nil {
fmt.Println("error:", err)
}
fmt.Printf("%+v", animals)

Output:

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

easyjson, ffjson

easyjson, ffjson 并没有使用反射方式实现,而是在Go中为结构体生成静态

MarshalJSON
UnmarshalJSON
函数。生成的函数减少了对运行时反射的依赖,所以通常快2到3倍。但相比标准JSON包,使用起来略为繁琐。

使用步骤:

1、定义结构体,每个结构体注释里标注

//easyjson:json
或者
//ffjson: skip

2、使用
easyjson
或者
ffjson
命令将指定目录的go结构体文件生成带有
Marshal
UnMarshal
方法的新文件;
3、代码里如果需要进行生成JSON或者解析JSON,调用生成文件的
Marshal
UnMarshal
方法即可。

下面是使用示例。

easyjson

GitHub:https://github.com/mailru/easyjson

1、先安装:

go get -u github.com/mailru/easyjson/

2、定义结构体:

记得在需要使用

easyjson
的结构体上加上
//easyjson:json
。 如下:

//easyjson:json
type School struct {
Name string     `json:"name"`
Addr string     `json:"addr"`
}

//easyjson:json
type Student struct {
Id       int       `json:"id"`
Name     string    `json:"s_name"`
School   School    `json:"s_chool"`
Birthday time.Time `json:"birthday"`
}

3、在结构体包下执行

easyjson  -all student.go

此时在该目录下出现一个新的文件:easyjson_student.go,该文件给结构体增加了

MarshalJSON
UnmarshalJSON
等方法。

4、使用

package main

import (
"studygo/easyjson"
"time"
"fmt"
)

func main(){
s:=easyjson.Student{
Id: 11,
Name:"qq",
School:easyjson.School{
Name:"CUMT",
Addr:"xz",
},
Birthday:time.Now(),
}
bt,err:=s.MarshalJSON()
fmt.Println(string(bt),err)

json:=`{"id":11,"s_name":"qq","s_chool":{"name":"CUMT","addr":"xz"},"birthday":"2017-08-04T20:58:07.9894603+08:00"}`
ss:=easyjson.Student{}
ss.UnmarshalJSON([]byte(json))
fmt.Println(ss)
}

运行结果:

{"id":11,"s_name":"qq","s_chool":{"name":"CUMT","addr":"xz"},"birthday":"2017-08-04T20:58:07.9894603+08:00"} <nil>
{121  {CwwwwwwwUMT xzwwwww} 2017-08-04 20:52:03.4066002 +0800 CST}

ffjson

GitHub:https://github.com/pquerna/ffjson

本小节就不给示例了,大家直接看github上说明。用法与easyjson类似。

需要注意的是,ffjson也提供了

ffjson.Marshal
ffjson.Unmarshal
方法,如果没有使用ffjson给对应结构体生成静态的方法,则会调用标准库
encoding/json
进行编码解码:

func Marshal(v interface{}) ([]byte, error) {
//调用结构体的静态方法
f, ok := v.(marshalerFaster)
if ok {
buf := fflib.Buffer{}
err := f.MarshalJSONBuf(&buf)
b := buf.Bytes()
if err != nil {
if len(b) > 0 {
Pool(b)
}
return nil, err
}
return b, nil
}

//调用encoding/json
j, ok := v.(json.Marshaler)
if ok {
return j.MarshalJSON()
}
return json.Marshal(v)
}

go-simplejson, gabs, jason

这几个包都是在

encoding/json
的基础上进行开发的,为了是更方便的操作JSON:它不需要创建struct,而是动态按字段取内容。有时候我们仅仅想取JSON里的某个字段,用这个非常有用。

下面是

go-simplejson
示例。

go-simplejson

Github: https://github.com/bitly/go-simplejson

package main

import (
"fmt"
"github.com/bitly/go-simplejson"
)

func main() {

data := []byte(`{
"hits":{
"total":2,
"max_score":4.631368,
"hits":[
{
"_source":{
"account_number":298,
"balance":34334,
"firstname":"Bullock",
"lastname":"Marsh"
}
}
]
}
}`)

js, _ := simplejson.NewJson(data)

//get total
total, _ := js.Get("hits").Get("total").Int64()
fmt.Println(total)

account_number, _ := js.Get("hits").Get("hits").GetIndex(0).Get("_source").Get("account_number").Int64()
fmt.Println(account_number)

//get _source list
hitsjson, _ := js.Get("hits").Get("hits").MarshalJSON()
fmt.Printf("%s", hitsjson)
}

输出:

2
298
[{"_id":"298","_index":"bank","_score":4.631368,"_source":{"account_number":298,"balance":34334,"firstname":"Bullock","lastname":"Marsh"},"_type":"account"}]

go-simplejson 没有提供类似Each方法,无法对数组类型的进行遍历。但是我们可以将数组取到后调用

MarshalJSON
生成JSON,使用标准的
encoding/json
进行解析。

gabs

Github: https://github.com/Jeffail/gabs

package main

import (
"fmt"
"github.com/Jeffail/gabs/v2"
)

func main() {

data := []byte(`{}`) //注:为节省篇幅,data结构参考go-simplejson

js, _ := gabs.ParseJSON(data)

//get total
var total float64
//使用断言,否则类型错误会报错
if val, ok := js.Path("hits.total").Data().(float64); ok {
total = val
}
total2 := js.Search("hits", "total").Data().(float64)
total3 := js.S("hits", "total").Data().(float64) // S is shorthand for Search

gObj, _ := js.JSONPointer("/hits/total")
total4 := gObj.Data().(float64)
fmt.Println(total, total2, total3, total4)

exist := js.Exists("hits", "total")
fmt.Println(exist)

account_number := js.Path("hits.hits.0._source.account_number").Data().(float64)
fmt.Println(account_number)

//Iterating arrays
for _, v := range js.S("hits", "hits").Children() {
lastname := v.S("_source", "lastname").Data().(string)
fmt.Printf("%v\n", lastname)
}
}

输出:

2 2 2 2
true
298
Marsh

除此之外,gabs 还支持重新动态生成JSON、合并JSON等操作。但是解析需要使用断言这一点不是很方便。

jason

Github: https://github.com/antonholmquist/jason

示例:

package main

import (
"fmt"
"github.com/antonholmquist/jason"
)

func main() {

data := []byte(`{}`) //注:为节省篇幅,data结构参考go-simplejson

js, _ := jason.NewObjectFromBytes(data)

//get total
total, _ := js.GetInt64("hits", "total")
fmt.Println(total)

//get _source list
hitsjson, _ := js.GetObjectArray("hits", "hits")
for _, v := range hitsjson {
lastname, _ := v.GetString("_source", "lastname")
fmt.Printf("%v\n", lastname)
}
}

输出:

2
Marsh

提供了遍历数组的方法,但是没有提供按索引取某个数组的方法。

jsonparser

jsonparser 功能与

go-simplejson
类似,但是由于底层不是基于
encoding/json
开发的,官方宣称它比
encoding/json
快10倍。

GitHub: https://github.com/buger/jsonparser

下面是个解析ES的示例:

package main

import (
"encoding/json"
"fmt"
"github.com/buger/jsonparser"
)

type UserInfo struct {
AccountNumber int64  `json:"account_number"`
Balance       int64  `json:"balance"`
Firstname     string `json:"firstname"`
Lastname      string `json:"lastname"`
}

func main() {

data := []byte(`{}`) //注:为节省篇幅,data结构参考go-simplejson

//get total
total, _ := jsonparser.GetInt(data, "hits", "total")
fmt.Println(total)

//get _source list
var list []UserInfo
hitsjson, _, _, _ := jsonparser.Get(data, "hits", "hits")

type hitsMap struct {
Source UserInfo `json:"_source,omitempty"`
}

var hitsMaps []hitsMap
json.Unmarshal(hitsjson, &hitsMaps)

for _, info := range hitsMaps {
list = append(list, info.Source)
}
fmt.Printf("%+v\n", list)

//get each _source
jsonparser.ArrayEach(data, func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
_source, _, _, _ := jsonparser.Get(value, "_source")
fmt.Println(string(_source))
}, "hits", "hits")
}

输出:

2

[{AccountNumber:298 Balance:34334 Firstname:Bullock Lastname:Marsh}]

{
"account_number": 298,
"balance": 34334,
"firstname": "Bullock",
"lastname": "Marsh"
}

大家可以看一下 elastic/go-elasticsearch 给出的示例里是怎么解析JSON的:

// Print the response status, number of results, and request duration.
log.Printf(
"[%s] %d hits; took: %dms",
res.Status(),
int(r["hits"].(map[string]interface{})["total"].(map[string]interface{})["value"].(float64)),
int(r["took"].(float64)),
)
// Print the ID and document source for each hit.
for _, hit := range r["hits"].(map[string]interface{})["hits"].([]interface{}) {
log.Printf(" * ID=%s, %s", hit.(map[string]interface{})["_id"], hit.(map[string]interface{})["_source"])
}

对,就是使用的断言,这个会让人很崩溃,万一值不存在或者类型不对,还会直接扔个ERROR...

总结

大部分情况下大家直接使用

encoding/json
就行了,如果对性能要求很高的话,可以使用
easyjson, ffjson
。遇到解析ES搜索返回的复杂的JSON或者仅需要解析个别字段,
go-simplejson
或者
jsonparser
就很方便了。

参考

1、json - GoDoc
https://godoc.org/encoding/json#example-Unmarshal
2、Golang的json包一览 - 知乎
https://zhuanlan.zhihu.com/p/24451749
3、bitly/go-simplejson: a Go package to interact with arbitrary JSON
https://github.com/bitly/go-simplejson
4、buger/jsonparser: Alternative JSON parser for Go that does not require schema (so far fastest)
https://github.com/buger/jsonparser
5、Golang高性能json包:easyjson - 梦朝思夕的个人空间 - OSCHINA
https://my.oschina.net/qiangmzsx/blog/1503018
6、pquerna/ffjson: faster JSON serialization for Go
https://github.com/pquerna/ffjson
7、Jeffail/gabs: For parsing, creating and editing unknown or dynamic JSON in Go
https://github.com/Jeffail/gabs

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: