Skip to content

Commit b77a054

Browse files
update: 优化错误处理, 添加注释, 更新README; fix: 修复部分 Bug
1 parent 1dce32a commit b77a054

File tree

17 files changed

+666
-359
lines changed

17 files changed

+666
-359
lines changed

README.md

Lines changed: 122 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,30 @@
99
<a href="https://goreportcard.com/badge/github.com/520MianXiangDuiXiang520/json-diff"> <img src="https://goreportcard.com/badge/github.com/520MianXiangDuiXiang520/json-diff" /></a>
1010
<a href="https://codeclimate.com/github/520MianXiangDuiXiang520/json-diff/maintainability"><img src="https://api.codeclimate.com/v1/badges/ed575aea812a025dfcc9/maintainability" /></a>
1111

12+
```shell
13+
go get -u github.com/520MianXiangDuiXiang520/json-diff
14+
```
15+
1216
## 功能:
1317

14-
* 不依赖于 `struct` 的 JSON 的序列化与反序列化
15-
* 两个 JSON 串的差异比较
16-
* 根据差异还原原 JSON 串
18+
### 序列化与反序列化
1719

18-
## 使用
20+
与官方 json 包的序列化和反序列化不同,官方包序列化需要指定一个 `interface{}`, 像:
1921

20-
```shell
21-
go get -u github.com/520MianXiangDuiXiang520/json-diff
22+
```go
23+
package main
24+
25+
import "json"
26+
27+
func main() {
28+
jsonStr := "{}"
29+
var jsonObj interface{}
30+
node := json.Unmarshal(&jsonObj, []byte(jsonStr))
31+
// ...
32+
}
2233
```
2334

24-
### 序列化与反序列化
35+
这样不方便编辑反序列化后的 json 对象, json-diff 可以将任意的 json 串转换成统一的 `JsonNode` 类型,并且提供一系列的增删查改方法,方便操作对象:
2536

2637
```go
2738
func ExampleUnmarshal() {
@@ -37,31 +48,54 @@ func ExampleUnmarshal() {
3748
}
3849
```
3950

40-
`Unmarshal()` 将任意合法的 JSON 串反序列化成 `*JsonNode` 类型,基于该类型,可以更方便地操作 JSON 对象
41-
42-
`JsonNode` 包含以下方法:
43-
44-
* `Find(path string)`: 从当前 `JsonNode` 中找到满足 path 的子对象并返回, 如要查找 `CAA`, path 为 `/C/CA/CAA`
45-
* `Equal(*JsonNode)`: 判断两个对象是否相等
46-
* `ADD(key interface{}, value *JsonNode)`: 根据 key 向当前对象插入一个子对象
47-
* `Replace(key interface{}, value *JsonNode)`: 根据 key 替换
48-
* `Remove(key interface{})`: 根据 key shanc
49-
* ...
51+
### 差异比较
5052

51-
`JsonNode` 对象可以使用 `Marshal()` 方法序列化成 JSON 字符数组
53+
通过对比两个 Json 串,输出他们的差异或者通过差异串得到修改后的 json 串
5254

53-
### diff
55+
```go
56+
func ExampleAsDiffs() {
57+
json1 := `{
58+
"A": 1,
59+
"B": [1, 2, 3],
60+
"C": {
61+
"CA": 1
62+
}
63+
}`
64+
json2 := `{
65+
"A": 2,
66+
"B": [1, 2, 4],
67+
"C": {
68+
"CA": {"CAA": 1}
69+
}
70+
}`
71+
res, _ := AsDiffs([]byte(json1), []byte(json2), UseMoveOption, UseCopyOption, UseFullRemoveOption)
72+
fmt.Println(res)
73+
}
74+
```
5475

55-
diff 遵循 RFC 6092 规范,两个 JSON 串的差异被分为 6 类:
76+
```go
77+
func ExampleMergeDiff() {
78+
json2 := `{
79+
"A": 1,
80+
"B": [1, 2, 3, {"BA": 1}],
81+
"C": {
82+
"CA": 1,
83+
"CB": 2
84+
}
85+
}`
86+
diffs := `[
87+
{"op": "move", "from": "/A", "path": "/D"},
88+
{"op": "move", "from": "/B/0", "path": "/B/1"},
89+
{"op": "move", "from": "/B/2", "path": "/C/CB"}
90+
]`
91+
res, _ := MergeDiff([]byte(json2), []byte(diffs))
92+
fmt.Println(res)
93+
}
94+
```
5695

57-
1. `add`: 新增
58-
2. `replace`: 替换
59-
3. `remove`: 删除
60-
4. `move`: 移动
61-
5. `copy`: 复制
62-
6. `test`: 测试
96+
#### 输出格式
6397

64-
他们的格式如下
98+
输出一个 json 格式的字节数组,类似于
6599

66100
```json
67101
[
@@ -74,60 +108,80 @@ diff 遵循 RFC 6092 规范,两个 JSON 串的差异被分为 6 类:
74108
]
75109
```
76110

77-
> * `test` 用于还原时测试路径下的值是否与 value 相等
111+
其中数组中的每一项代表一个差异点,格式由 RFC 6092 定义,op 表示差异类型,有六种:
112+
113+
1. `add`: 新增
114+
2. `replace`: 替换
115+
3. `remove`: 删除
116+
4. `move`: 移动
117+
5. `copy`: 复制
118+
6. `test`: 测试
119+
120+
其中 move 和 copy 可以减少差异串的体积,但会增加差异比较的时间, 可以通过修改 `AsDiff()` 的 options 指定是否开启,options 的选项和用法如下:
78121

79122
```go
80-
func ExampleAsDiffs() {
81-
json1 := `{
82-
"A": 1,
83-
"B": [1, 2, 3],
84-
"C": {
85-
"CA": 1
86-
}
87-
}`
88-
json2 := `{
89-
"A": 2,
90-
"B": [1, 2, 4],
91-
"C": {
92-
"CA": {"CAA": 1}
93-
}
94-
}`
95-
res, _ := AsDiffs([]byte(json1), []byte(json2))
96-
fmt.Println(res)
123+
// 返回差异时使用 Copy, 当发现新增的子串出现在原串中时,使用该选项可以将 Add 行为替换为 Copy 行为
124+
// 以减少差异串的大小,但这需要额外的计算,默认不开启
125+
UseCopyOption JsonDiffOption = 1 << iota
126+
127+
// 仅在 UseCopyOption 选项开启时有效,替换前会添加 Test 行为,以确保 Copy 的路径存在
128+
UseCheckCopyOption
129+
130+
// 返回差异时使用 Copy, 当发现差异串中两个 Add 和 Remove 的值相等时,会将他们合并为一个 Move 行为
131+
// 以此减少差异串的大小,默认不开启
132+
UseMoveOption
133+
134+
// Remove 时除了返回 path, 还返回删除了的值,默认不开启
135+
UseFullRemoveOption
136+
```
137+
138+
#### 相等的依据
139+
140+
对于一个对象,其内部元素的顺序不作为相等判断的依据,如
141+
142+
```json
143+
{
144+
"a": 1,
145+
"b": 2,
97146
}
98147
```
99148

100-
使用 `AsDiffs()` 函数获取两个 JSON 串的差异,`AsDiffs()` 接收一组可选的参数 `JsonDiffOption` 他们的取值如下:
149+
101150

102-
```go
103-
// 返回差异时使用 Copy, 当发现新增的子串出现在原串中时,使用该选项可以将 Add 行为替换为 Copy 行为
104-
// 以减少差异串的大小,但这需要额外的计算,默认不开启
105-
UseCopyOption JsonDiffOption = 1 << iota
106-
107-
// 仅在 UseCopyOption 选项开启时有效,替换前会添加 Test 行为,以确保 Copy 的路径存在
108-
UseCheckCopyOption
109-
110-
// 返回差异时使用 Copy, 当发现差异串中两个 Add 和 Remove 的值相等时,会将他们合并为一个 Move 行为
111-
// 以此减少差异串的大小,默认不开启
112-
UseMoveOption
113-
114-
// Remove 时除了返回 path, 还返回删除了的值,默认不开启
115-
UseFullRemoveOption
151+
```json
152+
{
153+
"b": 2,
154+
"a": 1,
155+
}
116156
```
117157

118-
即默认情况下,差异串只有 `add, replace, remove` 三种, `remove` 也只会返回 path, 更改默认行为可以传入需要的 JsonDiffOption, 如:
158+
被认为是相等的。
119159

120-
```go
121-
res, _ := AsDiffs([]byte(json1), []byte(json2), UseMoveOption, UseCopyOption, UseFullRemoveOption)
122-
fmt.Println(res)
160+
对于一个列表,元素顺序则作为判断相等的依据,如:
161+
162+
```json
163+
{
164+
"a": [1, 2]
165+
}
166+
```
167+
168+
169+
170+
```json
171+
{
172+
"a": [2, 1]
173+
}
123174
```
124175

125-
## 其他
176+
被认为不相等。
177+
178+
只有一个元素的所有子元素全部相等,他们才相等
179+
180+
#### 原子性
126181

127-
什么样的两个 JSON 对象被认为是相等的:
182+
根据 RFC 6092,差异合并应该具有原子性,即列表中有一个差异合并失败,之前的合并全部作废,而 test 类型就用来在合并差异之前检查路径和值是否正确,你可以通过选项开启它,但即便不使用 test,合并也是原子性的。
128183

129-
* 对于一个对象 `{}`,顺序无关
130-
* 对于一个列表 `[]`, 顺序相关
184+
json-diff 在合并差异前会深拷贝源数据,并使用拷贝的数据做差异合并,一旦发生错误,将会返回 nil, 任何情况下都不会修改原来的数据。
131185

132186
## 参考
133187

deepcopy_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ func TestDeepCopy(t *testing.T) {
1111
if err != nil {
1212
t.Error("fail to open the ", fileName)
1313
}
14-
src := Unmarshal(input)
14+
src, _ := Unmarshal(input)
1515
type args struct {
1616
dst interface{}
1717
src interface{}

diffdoc.go

Lines changed: 0 additions & 65 deletions
This file was deleted.

0 commit comments

Comments
 (0)