|
| 1 | +# Json-Diff |
| 2 | + |
| 3 | +[RFC 6092](https://tools.ietf.org/html/rfc6902) 的 Go 语言实现 |
| 4 | + |
| 5 | +## 功能: |
| 6 | + |
| 7 | +* 不依赖于 `struct` 的 JSON 的序列化与反序列化 |
| 8 | +* 两个 JSON 串的差异比较 |
| 9 | +* 根据差异还原原 JSON 串 |
| 10 | + |
| 11 | +## 使用 |
| 12 | + |
| 13 | +```shell |
| 14 | +go get -u github.com/520MianXiangDuiXiang520/json-diff |
| 15 | +``` |
| 16 | + |
| 17 | +### 序列化与反序列化 |
| 18 | + |
| 19 | +```go |
| 20 | +func ExampleUnmarshal() { |
| 21 | + json := `{ |
| 22 | + "A": 2, |
| 23 | + "B": [1, 2, 4], |
| 24 | + "C": { |
| 25 | + "CA": {"CAA": 1} |
| 26 | + } |
| 27 | + }` |
| 28 | + jsonNode := Unmarshal([]byte(json)) |
| 29 | + fmt.Println(jsonNode) |
| 30 | +} |
| 31 | +``` |
| 32 | + |
| 33 | +`Unmarshal()` 将任意合法的 JSON 串反序列化成 `*JsonNode` 类型,基于该类型,可以更方便地操作 JSON 对象 |
| 34 | + |
| 35 | +`JsonNode` 包含以下方法: |
| 36 | + |
| 37 | +* `Find(path string)`: 从当前 `JsonNode` 中找到满足 path 的子对象并返回, 如要查找 `CAA`, path 为 `/C/CA/CAA` |
| 38 | +* `Equal(*JsonNode)`: 判断两个对象是否相等 |
| 39 | +* `ADD(key interface{}, value *JsonNode)`: 根据 key 向当前对象插入一个子对象 |
| 40 | +* `Replace(key interface{}, value *JsonNode)`: 根据 key 替换 |
| 41 | +* `Remove(key interface{})`: 根据 key shanc |
| 42 | +* ... |
| 43 | + |
| 44 | +`JsonNode` 对象可以使用 `Marshal()` 方法序列化成 JSON 字符数组 |
| 45 | + |
| 46 | +### diff |
| 47 | + |
| 48 | +diff 遵循 RFC 6092 规范,两个 JSON 串的差异被分为 6 类: |
| 49 | + |
| 50 | +1. `add`: 新增 |
| 51 | +2. `replace`: 替换 |
| 52 | +3. `remove`: 删除 |
| 53 | +4. `move`: 移动 |
| 54 | +5. `copy`: 复制 |
| 55 | +6. `test`: 测试 |
| 56 | + |
| 57 | +他们的格式如下: |
| 58 | + |
| 59 | +```json |
| 60 | + [ |
| 61 | + { "op": "test", "path": "/a/b/c", "value": "foo" }, |
| 62 | + { "op": "remove", "path": "/a/b/c" }, |
| 63 | + { "op": "add", "path": "/a/b/c", "value": [ "foo", "bar" ] }, |
| 64 | + { "op": "replace", "path": "/a/b/c", "value": 42 }, |
| 65 | + { "op": "move", "from": "/a/b/c", "path": "/a/b/d" }, |
| 66 | + { "op": "copy", "from": "/a/b/d", "path": "/a/b/e" } |
| 67 | + ] |
| 68 | +``` |
| 69 | + |
| 70 | +> * `test` 用于还原时测试路径下的值是否与 value 相等 |
| 71 | +
|
| 72 | +```go |
| 73 | +func ExampleAsDiffs() { |
| 74 | + json1 := `{ |
| 75 | + "A": 1, |
| 76 | + "B": [1, 2, 3], |
| 77 | + "C": { |
| 78 | + "CA": 1 |
| 79 | + } |
| 80 | + }` |
| 81 | + json2 := `{ |
| 82 | + "A": 2, |
| 83 | + "B": [1, 2, 4], |
| 84 | + "C": { |
| 85 | + "CA": {"CAA": 1} |
| 86 | + } |
| 87 | + }` |
| 88 | + res, _ := AsDiffs([]byte(json1), []byte(json2)) |
| 89 | + fmt.Println(res) |
| 90 | +} |
| 91 | +``` |
| 92 | + |
| 93 | +使用 `AsDiffs()` 函数获取两个 JSON 串的差异,`AsDiffs()` 接收一组可选的参数 `JsonDiffOption` 他们的取值如下: |
| 94 | + |
| 95 | +```go |
| 96 | + // 返回差异时使用 Copy, 当发现新增的子串出现在原串中时,使用该选项可以将 Add 行为替换为 Copy 行为 |
| 97 | + // 以减少差异串的大小,但这需要额外的计算,默认不开启 |
| 98 | + UseCopyOption JsonDiffOption = 1 << iota |
| 99 | + |
| 100 | + // 仅在 UseCopyOption 选项开启时有效,替换前会添加 Test 行为,以确保 Copy 的路径存在 |
| 101 | + UseCheckCopyOption |
| 102 | + |
| 103 | + // 返回差异时使用 Copy, 当发现差异串中两个 Add 和 Remove 的值相等时,会将他们合并为一个 Move 行为 |
| 104 | + // 以此减少差异串的大小,默认不开启 |
| 105 | + UseMoveOption |
| 106 | + |
| 107 | + // Remove 时除了返回 path, 还返回删除了的值,默认不开启 |
| 108 | + UseFullRemoveOption |
| 109 | +``` |
| 110 | + |
| 111 | +即默认情况下,差异串只有 `add, replace, remove` 三种, `remove` 也只会返回 path, 更改默认行为可以传入需要的 JsonDiffOption, 如: |
| 112 | + |
| 113 | +```go |
| 114 | +res, _ := AsDiffs([]byte(json1), []byte(json2), UseMoveOption, UseCopyOption, UseFullRemoveOption) |
| 115 | +fmt.Println(res) |
| 116 | +``` |
| 117 | + |
| 118 | +## 其他 |
| 119 | + |
| 120 | +什么样的两个 JSON 对象被认为是相等的: |
| 121 | + |
| 122 | +* 对于一个对象 `{}`,顺序无关 |
| 123 | +* 对于一个列表 `[]`, 顺序相关 |
| 124 | + |
| 125 | +## 参考 |
| 126 | + |
| 127 | +[https://github.com/flipkart-incubator/zjsonpatch](https://github.com/flipkart-incubator/zjsonpatch) |
0 commit comments