持续创作,加速成长!这是我参与「日新方案 10 月更文挑战」的第3天,点击检查活动概况
上一篇文章中 解析 Golang 读取 json 神器 gjson 中咱们学习了 gjson。今日这篇咱们来看看它的好伙伴:sjson。二者都是出自 tidwall 开源的项目。强烈建议咱们看了前一篇后再来看 sjson,这样了解起来会快许多。
gjson 代表的是【get json】,sjson 代表的则是【set json】,也便是对 json 进行更新。
有的时分咱们期望对一个大 json 文档中的部分进行修改,这一点使用官方 encoding/json 也是能够完结的,但仍是和 gjson 相似的问题。你需求这样几步:
- 界说一个 struct,对应到 json 的结构;
- json => 结构体的反序列化,得到一个带数据的结构体;
- 更新你期望修改的部分数据;
- 结构体从头序列化为 json。
1 带来的开发本钱或许是一次性的,但 2 和 4 这里的序列化本钱却是每次都需求支付的,这一点很痛。
所以,tidwall 在 gjson 之外补充了 sjson 来弥补对 json 文档进行部分更新的能力。
sjsonSJSON 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 库,做到了部分读写,不必对整个文档进行序列化和反序列化。
首要咱们用 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 字符串。
Pathgjson.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"
有两个点需求注意:
- 在更新的场景下,有时分咱们需求【刺进】一个新的元素,这个时分能够用 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。