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
2738func 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
0 commit comments