持续创作,加速成长!这是我参与「日新方案 10 月更文挑战」的第3天,点击检查活动概况

上一篇文章中 解析 Golang 读取 json 神器 gjson 中咱们学习了 gjson。今日这篇咱们来看看它的好伙伴:sjson。二者都是出自 tidwall 开源的项目。强烈建议咱们看了前一篇后再来看 sjson,这样了解起来会快许多。

gjson 代表的是【get json】,sjson 代表的则是【set json】,也便是对 json 进行更新。

有的时分咱们期望对一个大 json 文档中的部分进行修改,这一点使用官方 encoding/json 也是能够完结的,但仍是和 gjson 相似的问题。你需求这样几步:

  1. 界说一个 struct,对应到 json 的结构;
  2. json => 结构体的反序列化,得到一个带数据的结构体;
  3. 更新你期望修改的部分数据;
  4. 结构体从头序列化为 json。

1 带来的开发本钱或许是一次性的,但 2 和 4 这里的序列化本钱却是每次都需求支付的,这一点很痛。

所以,tidwall 在 gjson 之外补充了 sjson 来弥补对 json 文档进行部分更新的能力。

sjson

SJSON is a Go package that provides avery fastand simple way to set a value in a json document. For quickly retrieving json values check outGJSON.

sjson 支撑以简略快速的办法来设置 json 中的值。是不是感觉似曾相识,其实跟 gjson 是一样的,二者也经常配合使用。这个系列的两兄弟最大的特点便是:

  • 简略:不需求你界说结构体,有 json 文档,界说好规则就能读写;
  • 快速:功能上优势巨大,只依靠原生 Golang 库,做到了部分读写,不必对整个文档进行序列化和反序列化。
Demo

首要咱们用 go get 给自己的工程添加 sjson 依靠:

$ go get -u github.com/tidwall/sjson

履行如下代码:

package main
import "github.com/tidwall/sjson"
const json = `{"name":{"first":"Janet","last":"Prichard"},"age":47}`
func main() {
	value, _ := sjson.Set(json, "name.last", "Anderson")
	println(value)
}

打印出来的结果为:

{"name":{"first":"Janet","last":"Anderson"},"age":47}
gjson.Get(json, path)sjson.Set(json, path, value)

咱们拿到的 value 是个 string,语义上代表被更新后的 json 字符串。

Path
gjson.Getsjson.Set

咱们复习一下,假设有如下 json 文档:

{
  "name": {"first": "Tom", "last": "Anderson"},
  "age":37,
  "children": ["Sara","Alex","Jack"],
  "fav.movie": "Deer Hunter",
  "friends": [
	{"first": "James", "last": "Murphy"},
	{"first": "Roger", "last": "Craig"}
  ]
}

示例 path 对应 结果如下:

"name.last"          >> "Anderson"
"age"                >> 37
"children.1"         >> "Alex"
"friends.1.last"     >> "Craig"

有两个点需求注意:

  1. 在更新的场景下,有时分咱们需求【刺进】一个新的元素,这个时分能够用 index 下标 -1 来代表这个语义:
"children.-1"  >> appends a new value to the end of the children array
:
{
  "users":{
    "2313":{"name":"Sara"},
    "7839":{"name":"Andy"}
  }
}
"users.:2313.name"    >> "Sara"
支撑的类型

sjson 支撑将下面这些类型更新到 json 文档中:

  • nil
  • boolean: true, false
  • 整数
  • 浮点数
  • 字符串
  • map[string]interface{}

若 sjson 未识别到,将会 fallback 到 encoding/json 的 Marshaller 进行序列化。

sjson.Set(`{"key":true}`, "key", nil)
sjson.Set(`{"key":true}`, "key", false)
sjson.Set(`{"key":true}`, "key", 1)
sjson.Set(`{"key":true}`, "key", 10.5)
sjson.Set(`{"key":true}`, "key", "hello")
sjson.Set(`{"key":true}`, "key", []string{"hello", "world"})
sjson.Set(`{"key":true}`, "key", map[string]interface{}{"hello":"world"})
常见用法

初始化一个 json 文档

value, _ := sjson.Set("", "name", "Tom")
println(value)
// Output:
// {"name":"Tom"}
value, _ := sjson.Set("", "name.last", "Anderson")
println(value)
// Output:
// {"name":{"last":"Anderson"}}

新增特点

value, _ := sjson.Set(`{"name":{"last":"Anderson"}}`, "name.first", "Sara")
println(value)
// Output:
// {"name":{"first":"Sara","last":"Anderson"}}

更新现已存在的特点

value, _ := sjson.Set(`{"name":{"last":"Anderson"}}`, "name.last", "Smith")
println(value)
// Output:
// {"name":{"last":"Smith"}}

往 array 中新增一个元素

value, _ := sjson.Set(`{"friends":["Andy","Carol"]}`, "friends.2", "Sara")
println(value)
// Output:
// {"friends":["Andy","Carol","Sara"]

或者使用咱们前面提到的 -1 下标

value, _ := sjson.Set(`{"friends":["Andy","Carol"]}`, "friends.-1", "Sara")
println(value)
// Output:
// {"friends":["Andy","Carol","Sara"]

注意,-1 是自动往末尾加,假如清晰下标,如上面的 friends.2,那么一定要保证 2 便是最终的下标,否则会拆入 null:

value, _ := sjson.Set(`{"friends":["Andy","Carol"]}`, "friends.4", "Sara")
println(value)
// Output:
// {"friends":["Andy","Carol",null,null,"Sara"]

删除特点

value, _ := sjson.Delete(`{"name":{"first":"Sara","last":"Anderson"}}`, "name.first")
println(value)
// Output:
// {"name":{"last":"Anderson"}}

删除 array 元素

  • 指定下标
value, _ := sjson.Delete(`{"friends":["Andy","Carol"]}`, "friends.1")
println(value)
// Output:
// {"friends":["Andy"]}
  • 指定最终一个
value, _ := sjson.Delete(`{"friends":["Andy","Carol"]}`, "friends.-1")
println(value)
// Output:
// {"friends":["Andy"]}
高档用法

除了相似 gjson 中的 Bytes 函数,削减内存分配外,sjson 还支撑了一些 option 来细粒度控制,咱们来看一下:

// Options represents additional options for the Set and Delete functions.
type Options struct {
	// Optimistic is a hint that the value likely exists which
	// allows for the sjson to perform a fast-track search and replace.
	Optimistic bool
	// ReplaceInPlace is a hint to replace the input json rather than
	// allocate a new json byte slice. When this field is specified
	// the input json will not longer be valid and it should not be used
	// In the case when the destination slice doesn't have enough free
	// bytes to replace the data in place, a new bytes slice will be
	// created under the hood.
	// The Optimistic flag must be set to true and the input must be a
	// byte slice in order to use this field.
	ReplaceInPlace bool
}
  • Optimistic: 指明操作的值很大或许是现已存在的,这样允许 sjson 针对性地做一些优化,功能更优;
  • ReplaceInPlace: 指明更新 json 在原地完结,而不是创立出来新的 []byte 来承接 json,这样会有更好的功能收益。若开启,不能再依靠本来的 input json 内存地址,或许出现扩容。强依靠 Optimistic 为 true 才能启用。

事实上,这两个选项都是开发者声明,用以标明自己能接受多大程度的优化,经过官方benchmark咱们也能看到,功能收益仍是很可观的:

Benchmark_SJSON-8                  	 3000000	       805 ns/op	    1077 B/op	       3 allocs/op
Benchmark_SJSON_ReplaceInPlace-8   	 3000000	       449 ns/op	       0 B/op	       0 allocs/op
Benchmark_JSON_Map-8               	  300000	     21236 ns/op	    6392 B/op	     150 allocs/op
Benchmark_JSON_Struct-8            	  300000	     14691 ns/op	    1789 B/op	      24 allocs/op
Benchmark_Gabs-8                   	  300000	     21311 ns/op	    6752 B/op	     150 allocs/op
Benchmark_FFJSON-8                 	  300000	     17673 ns/op	    3589 B/op	      47 allocs/op
Benchmark_EasyJSON-8               	 1500000	      3119 ns/op	    1061 B/op	      13 allocs/op

开启了 ReplaceInPlace 后,耗时几乎变成了本来的一半,仍是很厉害的。感兴趣的同学能够看一下 sjson-benchmark。