Go语言进阶之路(六):内置JSON库和开源库gjson

Go语言内置了部分JSON函数,可以方便地在Go语言结构体实例和JSON字符串之间互相转换。这可比Java强多了。
 
不过Go语言内置的json库功能比较鸡肋,只能在结构体和JSON之间相互转换,没办法满足在JSON字符串中进行条件匹配和搜索的功能。本文先介绍Go语言内置的json库,随后介绍了功能更强大的gjson库。
 
阅读本文预计不用超过30分钟。
 

一 内置库json

1.1 json.Marshal

Marshal函数,把Go对象转换成JSON字节切片。看一下Marshal函数定义:
func Marshal(v interface{}) ([]byte, error)

典型使用案例:

type Message struct {
    Name string
    Body string
    Time int64
}
m := Message{"Alice", "Hello", 1294706395881547000}
b, err := json.Marshal(m)
fmt.Println(b)
b2, err := json.MarshalIndent(m, "", "    ")
fmt.Println(b2)

Marshal以紧凑的方式输出JSON到字节切片中,MarshalIndent以缩进方式输出JSON到字节切片中。上面例子的输出为:

{"Name":"Alice","Body":"Hello","Time":1294706395881547000}
{
    "Name": "Alice",
    "Body": "Hello",
    "Time": 1294706395881547000
}

1.2 json.Unmarshal

Unmarshal函数,把JSON字节切片转换成Go对象(不可导出的变量无法解析):
func Unmarshal(data [] byte, v interface{}) error

典型使用案例:

b := []byte(`{"Name":"Bob","Food":"Pickle"}`)
var m Message
err := json.Unmarshal(b, &m)

Unmarshal函数会把传入的data字节切片作为一个JSON来进行解析,解析后的数据存储在参数v中。这个参数v也是任意类型的参数(但一定要是一个类型的指针),原因是我们在是以此函数进行JSON解析的时候,这个函数不知道这个传入参数的具体类型,所以它需要接收所有的类型(v interface{})。

注意,不可导出的属性没办法输出到JSON字符串中去,比如私有属性:
type People struct {
    name string `json:"Name"`  // 注意,name是私有字段,无法从json字符串中解析出来
}

func main() {
    js := `{
        "Name":"11"
    }`
    var p People
    err := json.Unmarshal([]byte(js), &p)
    if err != nil {
        fmt.Println("err: ", err)
        return
    }
    fmt.Println("people: ", p)  // 输出:people: {}
}

除了Marshal和Unmarshal,json包下面还包含了NewEncoder和NewDecoder,方便把JSON字符串从Reader和Writer中解析和输出。

1.3 json.NewEncoder&Encoder.Encode

json.Encoder,将Encode方法参数的Go结构体对象,编码成JSON内容输出到Writer中。
第一步,使用json.NewEncoder创建一个新的Encoder:
func NewEncoder(w io.Writer) *Encoder

第二步,使用Encoder的Encode方法将Go结构体对象编码成JSON字符串输出到Encoder中。

func (enc *Encoder) Encode(v interface{}) error

典型使用案例:

file, _ := os.Create("json.txt")  // 创建一个文件Writer
enc := json.NewEncoder(file)  // 创建这个文件Writer对象的Encoder
err := enc.Encode(&v)   // v是一个go结构体对象。把v对象转换为JSON格式写进file这个Writer里面

1.4 json.NewDecoder&Decoder.Decode

Decoder,将Reader中的JSON内容解码成Go对象,存入Decode方法的参数中,Decode方法的参数一般传入对象的地址(指针类型)。
第一步,使用json.NewDecoder创建一个新的Decoder:
func NewDecoder(r io.Reader) *Decoder

第二步,使用Decoder的Decode方法将Decoder中Reader的JSON字符串解析成Go结构体对象,存入参数v中。

func (dec *Decoder) Decode(v interface{}) error

典型使用案例:

fp, _ := os.Open("json.txt")  // 创建一个文件Reader
dec := json.NewDecoder(fp)  // 创建一个文件Reader的Decoder
for {
    var V v
    err := dec.Decode(&v)
    if err != nil {
        break
    }
    //use v
}

 

二 开源库gjson

用过Go语言的都知道,在项目中Go语言json包肯定是不足以满足千变万化的需求的。Go语言中Unmarshal函数会把整个JSON字符串解析成Go语言结构体实例,如果这个实例很大,JSON很长,我们只需要用到这个实例中的一个属性,那么这个转换就浪费了很多性能。所以,在项目中大家一般会使用第三方JSON包。

2.1 gjson

gjson是Github上很受欢迎的Go语言JSON开源库,可以通过级联方式直接获取下级某个属性,可以获取某个JSON数组的第一个元素,最后一个元素,元素个数等等。gjson到现在已经有6000多star。
 

2.2 性能对比

gjson与内置JSON库、其他开源JSON库的性能对比结果,可以看到gjson性能远远优于内置json库和其他开源库:

 

2.3 级联获取下级属性

gjson可以通过xx.xx直接获取某个下级属性,不需要把整个JSON解析成Go结构体实例后再获取:
import (
  "fmt"
  "github.com/tidwall/gjson"
)
const json = `{"name":{"first":"Janet","last":"Prichard"},"age":47}`

func main() {
  value := gjson.Get(json, "name.last")  // 直接获取name中的last属性值
  fmt.Println(value.String())  // 输出:Prichard
}

2.4 获取JSON数组元素、元素个数、第一个元素:

gjson可以通过.#获取JSON数组元素个数,.1获取JSON数组的第一个元素,可以通过*来匹配JSON字段名:
const json = `{
  "name": {"first": "Tom", "last": "Anderson"},
  "age":37,
  "children": ["Sara","Alex","Jack"]
}`

func main() {
  value := gjson.Get(json, "children")  // 获取children数组
  fmt.Println(value.String())  // 输出:["Sara","Alex","Jack"]

  value = gjson.Get(json, "children.#")  // 获取children数组元素个数
  fmt.Println(value.String())  // 输出:3

  value = gjson.Get(json, "children.1")  // 获取children数组的第二个元素(index为1)
  fmt.Println(value.String())  // 输出:Alex

  value = gjson.Get(json, "child*.2")  // 获取child*数组的第三个元素(index为2),*可以匹配任意多个字符,此处能匹配到children数组
  fmt.Println(value.String())  // 输出:Jack
}

2.5 获取JSON数组下所有元素的某个属性值,获取JSON数组下符合匹配条件的所有元素的某个属性值

可以使用#(...)来寻找数组中第一个匹配的元素,使用#(...)#寻找数组中所有匹配的元素:
const json = `{
  "name": {"first": "Tom", "last": "Anderson"},
  "friends": [
    {"first_name": "Dale", "last_name": "Murphy", "age": 44, "nets": ["ig", "fb", "tw"]},
    {"first_name": "Roger", "last_name": "Craig", "age": 68, "nets": ["fb", "tw"]},
    {"first_name": "Jane", "last_name": "Murphy", "age": 47, "nets": ["ig", "tw"]}
  ]
}`

func main() {
    // friends数组中每个元素都是一个JSON对象
  value := gjson.Get(json, "friends.#.first_name")  // #没有带表达式,用于获取数组的所有元素。此句匹配friends数组中所有元素对象的first_name属性。
  fmt.Println(value.String())  // 输出:["Dale","Roger","Jane"]

  value = gjson.Get(json, "friends.1.first_name")  // 匹配friends数组中第二个元素对象(index为1)的first_name属性。
  fmt.Println(value.String())  // 输出:Roger

  value = gjson.Get(json, `friends.#(last_name=="Murphy").first_name`)  // #带表达式,用于获取符合匹配条件的第一个数组元素。此句匹配friends数组中last_name属性为Murphy的第一个数组元素的first_name属性。
  fmt.Println(value.String())  // 输出:Dale

  value = gjson.Get(json, `friends.#(age>45)#.last_name`)  // #带表达式,后面来跟了一个#,用于获取符合匹配条件的所有数组元素。此句匹配friends数组中age属性大于45的所有数组元素的last_name属性。
  fmt.Println(value.String())  // 输出:["Craig","Murphy"]
}

2.6 判断JSON属性是否存在

value := gjson.Get(json, "name.last")
if !value.Exists() {
  println("no last name")
} else {
  println(value.String())
}

2.7 判断JSON字符串是否合法的JSON

if !gjson.Valid(json) {
  return errors.New("invalid json")
}

2.8 使用修饰符定制获取结果

gjson支持使用修饰符来定制元素获取结果,在搜索表达式中加上管道符和修饰符即可,比如`children|@reverse|1`表示children数组逆序之后的第二个元素(index为1的那个元素)。来看一下例子:
const json = `{
  "name": {"first": "Tom", "last": "Anderson"},
  "friends": [
    {"first_name": "Dale", "last_name": "Murphy", "age": 44, "nets": ["ig", "fb", "tw"]},
    {"first_name": "Roger", "last_name": "Craig", "age": 68, "nets": ["fb", "tw"]},
    {"first_name": "Jane", "last_name": "Murphy", "age": 47, "nets": ["ig", "tw"]}
  ]
}`

func main() {
  value := gjson.Get(json, "friends.#.first_name|@reverse")  // 获取friends数组中所有元素对象的first_name属性,@reverse表示以逆序方式返回。
  fmt.Println(value.String())
}

另外gjson还支持用户自定义修饰符,只需要调用gjson.AddModifier函数即可,例如:

gjson.AddModifier("case", func(json, arg string) string {
  if arg == "upper" {
    return strings.ToUpper(json)
  }
  if arg == "lower" {
    return strings.ToLower(json)
  }
  return json
})

这样就给gjson增加了case修饰符,然后我就可以这样使用:

value := gjson.Get(json, "friends.#.first_name|@case:upper")
fmt.Println(value.String())

可以看到gjson的功能真是太强大。

 

喜欢的可以关注我的WeiXin订阅号,随时随地学习:

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值