evanphx/json-patch
GitHub: evanphx/json-patch
一个 Go 语言库,用于创建和应用符合 RFC6902 与 RFC7396 标准的 JSON 补丁,并支持文档比较与补丁合并。
Stars: 1221 | Forks: 194
# JSON-Patch
`jsonpatch` 是一个提供多种功能的库,既支持针对文档应用
[RFC6902 JSON 补丁](http://tools.ietf.org/html/rfc6902),
也支持计算并应用 [RFC7396 JSON 合并补丁](https://tools.ietf.org/html/rfc7396)。
[](http://godoc.org/github.com/evanphx/json-patch)
[](https://github.com/evanphx/json-patch/actions/workflows/go.yml)
[](https://goreportcard.com/report/github.com/evanphx/json-patch)
# 获取它!
**最新最强版本**:
```
go get -u github.com/evanphx/json-patch/v5
```
如果你需要第 4 版,请使用 `go get -u gopkg.in/evanphx/json-patch.v4`
(`v3` 之前的早期版本不可用)
# 使用它!
* [创建并应用合并补丁](#create-and-apply-a-merge-patch)
* [创建并应用 JSON 补丁](#create-and-apply-a-json-patch)
* [比较 JSON 文档](#comparing-json-documents)
* [合并合并补丁](#combine-merge-patches)
# 配置
* 有一个全局配置变量 `jsonpatch.SupportNegativeIndices`。
它默认为 `true`,启用了一项非标准实践,即允许负数索引表示从数组末尾开始计算的索引。可以通过设置 `jsonpatch.SupportNegativeIndices = false` 来禁用此功能。
* 有一个全局配置变量 `jsonpatch.AccumulatedCopySizeLimit`,它限制了补丁中 "copy" 操作导致的总大小增加(以字节为单位)。它默认为 0,表示没有限制。
这些全局变量控制着 `jsonpatch.Apply` 的行为。
`jsonpatch.Apply` 的替代方案是 `jsonpatch.ApplyWithOptions`,其行为由 `*jsonpatch.ApplyOptions` 类型的 `options` 参数控制。
`jsonpatch.ApplyOptions` 结构体包含了上述配置选项,
并增加了两个新选项:`AllowMissingPathOnRemove` 和 `EnsurePathExistsOnAdd`。
当 `AllowMissingPathOnRemove` 设置为 `true` 时,如果 `remove` 操作的 `path` 指向 JSON 文档中不存在的位置,`jsonpatch.ApplyWithOptions` 将忽略该操作。
`AllowMissingPathOnRemove` 默认为 `false`,这会导致在 `remove` 操作中遇到缺失的 `path` 时,`jsonpatch.ApplyWithOptions` 返回错误。
当 `EnsurePathExistsOnAdd` 设置为 `true` 时,`jsonpatch.ApplyWithOptions` 将确保
`add` 操作能够生成目标对象中缺失的所有 `path` 元素。
请使用 `jsonpatch.NewApplyOptions` 来创建一个 `jsonpatch.ApplyOptions` 实例,
其实例的值将根据全局配置变量进行填充。
## 创建并应用合并补丁
给定一个原始 JSON 文档和一个修改后的 JSON 文档,你可以创建
一个 [合并补丁](https://tools.ietf.org/html/rfc7396) 文档。
它可以描述将原始文档转换为修改后文档所需的更改。
一旦有了合并补丁,你就可以使用 `jsonpatch.MergePatch(document, patch)` 函数将其应用到其他 JSON 文档中。
```
package main
import (
"fmt"
jsonpatch "github.com/evanphx/json-patch"
)
func main() {
// Let's create a merge patch from these two documents...
original := []byte(`{"name": "John", "age": 24, "height": 3.21}`)
target := []byte(`{"name": "Jane", "age": 24}`)
patch, err := jsonpatch.CreateMergePatch(original, target)
if err != nil {
panic(err)
}
// Now lets apply the patch against a different JSON document...
alternative := []byte(`{"name": "Tina", "age": 28, "height": 3.75}`)
modifiedAlternative, err := jsonpatch.MergePatch(alternative, patch)
fmt.Printf("patch document: %s\n", patch)
fmt.Printf("updated alternative doc: %s\n", modifiedAlternative)
}
```
运行后,你会得到以下输出:
```
$ go run main.go
patch document: {"height":null,"name":"Jane"}
updated alternative doc: {"age":28,"name":"Jane"}
```
## 创建并应用 JSON 补丁
你可以使用 `DecodePatch([]byte)` 创建补丁对象,然后可以将其应用到 JSON 文档中。
以下是一个包含两个操作的补丁创建示例,
并将其应用到 JSON 文档中。
```
package main
import (
"fmt"
jsonpatch "github.com/evanphx/json-patch"
)
func main() {
original := []byte(`{"name": "John", "age": 24, "height": 3.21}`)
patchJSON := []byte(`[
{"op": "replace", "path": "/name", "value": "Jane"},
{"op": "remove", "path": "/height"}
]`)
patch, err := jsonpatch.DecodePatch(patchJSON)
if err != nil {
panic(err)
}
modified, err := patch.Apply(original)
if err != nil {
panic(err)
}
fmt.Printf("Original document: %s\n", original)
fmt.Printf("Modified document: %s\n", modified)
}
```
运行后,你会得到以下输出:
```
$ go run main.go
Original document: {"name": "John", "age": 24, "height": 3.21}
Modified document: {"age":24,"name":"Jane"}
```
## 比较 JSON 文档
由于可能存在空白字符和顺序上的差异,不能简单地直接比较 JSON 字符串或字节数组。
因此,你可以改用 `jsonpatch.Equal(document1, document2)` 来
判断两个 JSON 文档在 _结构上_ 是否相等。这会忽略
空白字符的差异以及键值对的顺序。
```
package main
import (
"fmt"
jsonpatch "github.com/evanphx/json-patch"
)
func main() {
original := []byte(`{"name": "John", "age": 24, "height": 3.21}`)
similar := []byte(`
{
"age": 24,
"height": 3.21,
"name": "John"
}
`)
different := []byte(`{"name": "Jane", "age": 20, "height": 3.37}`)
if jsonpatch.Equal(original, similar) {
fmt.Println(`"original" is structurally equal to "similar"`)
}
if !jsonpatch.Equal(original, different) {
fmt.Println(`"original" is _not_ structurally equal to "different"`)
}
}
```
运行后,你会得到以下输出:
```
$ go run main.go
"original" is structurally equal to "similar"
"original" is _not_ structurally equal to "different"
```
## 合并合并补丁
给定两个 JSON 合并补丁文档,可以将它们合并为一个
单独的合并补丁,该补丁可以同时描述两组更改。
生成的合并补丁可以按照如下方式使用:将其应用后所得的结果文档,
与依次将每个合并补丁应用到同一文档后所得的结果文档在结构上是相似的。
```
package main
import (
"fmt"
jsonpatch "github.com/evanphx/json-patch"
)
func main() {
original := []byte(`{"name": "John", "age": 24, "height": 3.21}`)
nameAndHeight := []byte(`{"height":null,"name":"Jane"}`)
ageAndEyes := []byte(`{"age":4.23,"eyes":"blue"}`)
// Let's combine these merge patch documents...
combinedPatch, err := jsonpatch.MergeMergePatches(nameAndHeight, ageAndEyes)
if err != nil {
panic(err)
}
// Apply each patch individual against the original document
withoutCombinedPatch, err := jsonpatch.MergePatch(original, nameAndHeight)
if err != nil {
panic(err)
}
withoutCombinedPatch, err = jsonpatch.MergePatch(withoutCombinedPatch, ageAndEyes)
if err != nil {
panic(err)
}
// Apply the combined patch against the original document
withCombinedPatch, err := jsonpatch.MergePatch(original, combinedPatch)
if err != nil {
panic(err)
}
// Do both result in the same thing? They should!
if jsonpatch.Equal(withCombinedPatch, withoutCombinedPatch) {
fmt.Println("Both JSON documents are structurally the same!")
}
fmt.Printf("combined merge patch: %s", combinedPatch)
}
```
运行后,你会得到以下输出:
```
$ go run main.go
Both JSON documents are structurally the same!
combined merge patch: {"age":4.23,"eyes":"blue","height":null,"name":"Jane"}
```
# 比较 JSON 文档的 CLI
你可以安装命令行程序 `json-patch`。
该程序可以接受多个 JSON 补丁文档作为参数,
并通过 `stdin` 接收 JSON 文档。它会将补丁应用到该
文档上,并输出修改后的文档。
**patch.1.json**
```
[
{"op": "replace", "path": "/name", "value": "Jane"},
{"op": "remove", "path": "/height"}
]
```
**patch.2.json**
```
[
{"op": "add", "path": "/address", "value": "123 Main St"},
{"op": "replace", "path": "/age", "value": "21"}
]
```
**document.json**
```
{
"name": "John",
"age": 24,
"height": 3.21
}
```
然后你可以运行:
```
$ go install github.com/evanphx/json-patch/cmd/json-patch
$ cat document.json | json-patch -p patch.1.json -p patch.2.json
{"address":"123 Main St","age":"21","name":"Jane"}
```
标签:EVTX分析, Go, JSON, JSON Patch, Ruby工具, 开发库, 数据合并, 日志审计