Skip to content

Commit 4f80582

Browse files
定制深拷贝方法,提高深拷贝速度
1 parent 577c27e commit 4f80582

File tree

5 files changed

+207
-78
lines changed

5 files changed

+207
-78
lines changed

deepcopy.go

+69-7
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,76 @@
11
package json_diff
22

33
import (
4-
"bytes"
5-
"encoding/gob"
4+
"github.com/pkg/errors"
65
)
76

8-
func DeepCopy(dst, src interface{}) error {
9-
var buf bytes.Buffer
10-
if err := gob.NewEncoder(&buf).Encode(src); err != nil {
11-
return err
7+
func copySlice(src *JsonNode) (*JsonNode, error) {
8+
size := len(src.Children)
9+
res := NewSliceNode(make([]*JsonNode, size), int(src.Level))
10+
for i, child := range src.Children {
11+
var newNode *JsonNode
12+
var err error
13+
switch child.Type {
14+
case JsonNodeTypeSlice:
15+
newNode, err = copySlice(child)
16+
if err != nil {
17+
return nil, errors.Wrapf(err, "fail to copy %dst of Slice type", i)
18+
}
19+
case JsonNodeTypeObject:
20+
newNode, err = copyObject(child)
21+
if err != nil {
22+
return nil, errors.Wrapf(err, "fail to copy %dst of Object type", i)
23+
}
24+
case JsonNodeTypeValue:
25+
newNode, err = copyValue(child)
26+
if err != nil {
27+
return nil, errors.Wrapf(err, "fail to copy %dst of Value type", i)
28+
}
29+
}
30+
res.Children[i] = newNode
1231
}
13-
return gob.NewDecoder(bytes.NewBuffer(buf.Bytes())).Decode(dst)
32+
return res, nil
33+
}
34+
35+
func copyValue(src *JsonNode) (*JsonNode, error) {
36+
return NewValueNode(src.Value, int(src.Level)), nil
37+
}
38+
39+
func copyObject(src *JsonNode) (*JsonNode, error) {
40+
res := NewObjectNode("", map[string]*JsonNode{}, int(src.Level))
41+
for k, v := range src.ChildrenMap {
42+
var newNode *JsonNode
43+
var err error
44+
switch v.Type {
45+
case JsonNodeTypeObject:
46+
newNode, err = copyObject(v)
47+
if err != nil {
48+
return nil, errors.Wrapf(err, "failed to copy %s of Object type", k)
49+
}
50+
case JsonNodeTypeSlice:
51+
newNode, err = copySlice(v)
52+
if err != nil {
53+
return nil, errors.Wrapf(err, "failed to copy %s of Slice type", k)
54+
}
55+
case JsonNodeTypeValue:
56+
newNode, err = copyValue(v)
57+
if err != nil {
58+
return nil, errors.Wrapf(err, "failed to copy %s of Value type", k)
59+
}
60+
}
61+
res.ChildrenMap[k] = newNode
62+
}
63+
return res, nil
64+
}
65+
66+
func DeepCopy(src *JsonNode) (*JsonNode, error) {
67+
switch src.Type {
68+
case JsonNodeTypeObject:
69+
return copyObject(src)
70+
case JsonNodeTypeSlice:
71+
return copySlice(src)
72+
case JsonNodeTypeValue:
73+
return copyValue(src)
74+
}
75+
return nil, errors.New("src has an unknown type")
1476
}

deepcopy_test.go

+57-30
Original file line numberDiff line numberDiff line change
@@ -5,40 +5,67 @@ import (
55
"testing"
66
)
77

8+
func TestDeepCopy_emptyObject(t *testing.T) {
9+
srcStr := "{}"
10+
srcNode, _ := Unmarshal([]byte(srcStr))
11+
cp, err := DeepCopy(srcNode)
12+
if err != nil {
13+
t.Errorf("got an error: %v", err)
14+
}
15+
err = cp.ADD("child", NewValueNode(1, 2))
16+
if err != nil {
17+
t.Errorf("fail to add child: %v", err)
18+
}
19+
if _, ok := srcNode.ChildrenMap["child"]; ok {
20+
t.Errorf("change source object: %s", srcNode.String())
21+
}
22+
}
23+
824
func TestDeepCopy(t *testing.T) {
9-
fileName := "./test_data/mergeSmoke.json"
25+
fileName := "./test_data/deepcopy_test.json"
1026
input, err := ioutil.ReadFile(fileName)
1127
if err != nil {
1228
t.Error("fail to open the ", fileName)
1329
}
14-
src, _ := Unmarshal(input)
15-
type args struct {
16-
dst interface{}
17-
src interface{}
18-
}
19-
dst := new(JsonNode)
20-
tests := []struct {
21-
name string
22-
args args
23-
wantErr bool
24-
}{
25-
{name: "smoke", args: args{
26-
dst: dst,
27-
src: src,
28-
}, wantErr: false},
29-
}
30-
for _, tt := range tests {
31-
t.Run(tt.name, func(t *testing.T) {
32-
if err := DeepCopy(tt.args.dst, tt.args.src); (err != nil) != tt.wantErr {
33-
t.Errorf("DeepCopy() error = %v, wantErr %v", err, tt.wantErr)
34-
}
35-
if dst == nil || !dst.Equal(src) {
36-
t.Errorf("Values are not equal after DeepCopy, dst is %v, but src is %v", dst, src)
37-
}
38-
dst.Value = "dst"
39-
if dst.Value == src.Value {
40-
t.Errorf("dst and src point to the same object")
41-
}
42-
})
30+
srcNode, _ := Unmarshal(input)
31+
cp, err := DeepCopy(srcNode)
32+
if err != nil {
33+
t.Errorf("got an error: %v", err)
34+
}
35+
err = cp.ADD("add_child", NewValueNode(1, 2))
36+
if err != nil {
37+
t.Errorf("fail to add child: %v", err)
38+
}
39+
err = cp.ADD("add_child", NewValueNode(1, 2))
40+
if err != nil {
41+
t.Errorf("fail to add child: %v", err)
42+
}
43+
// 更改深层 object
44+
path1 := "/obj/obj/c/ce/2/ceb"
45+
cpof, ok := cp.Find(path1)
46+
if !ok {
47+
t.Errorf("can not find: %s in cp node", path1)
48+
}
49+
cpof.Value = true
50+
of, ok := srcNode.Find(path1)
51+
if !ok {
52+
t.Errorf("can not find: %s in src node", path1)
53+
}
54+
if of.Value.(bool) {
55+
t.Errorf("change source object: %v", cpof.Value)
56+
}
57+
// 更改深层 slice
58+
path2 := "/obj/slice/4/bb/1"
59+
cpof, ok = cp.Find(path1)
60+
if !ok {
61+
t.Errorf("can not find: %s in cp node", path2)
62+
}
63+
cpof.Value = "ggg"
64+
of, ok = srcNode.Find(path2)
65+
if !ok {
66+
t.Errorf("can not find: %s in src node", path2)
67+
}
68+
if of.Value.(string) != "jjj" {
69+
t.Errorf("change source object: %v", cpof.Value)
4370
}
4471
}

jsondiff.go

+36-37
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
package json_diff
22

33
import (
4-
"bytes"
5-
"encoding/json"
6-
"fmt"
7-
"github.com/pkg/errors"
8-
"strconv"
4+
"bytes"
5+
"encoding/json"
6+
"fmt"
7+
"github.com/pkg/errors"
8+
"strconv"
99
)
1010

1111
func diffSlice(diffs *diffs, path string, source, patch *JsonNode, option JsonDiffOption) {
@@ -120,20 +120,20 @@ func diff(diffs *diffs, path string, source, patch *JsonNode, option JsonDiffOpt
120120

121121
// GetDiffNode 比较两个 JsonNode 之间的差异,并返回 JsonNode 格式的差异结果
122122
func GetDiffNode(sourceJsonNode, patchJsonNode *JsonNode, options ...JsonDiffOption) *JsonNode {
123-
option := JsonDiffOption(0)
124-
for _, o := range options {
125-
option |= o
126-
}
127-
diffs := newDiffs()
128-
diff(diffs, "", sourceJsonNode, patchJsonNode, option)
129-
doOption(diffs, option, sourceJsonNode, patchJsonNode)
130-
return diffs.d
123+
option := JsonDiffOption(0)
124+
for _, o := range options {
125+
option |= o
126+
}
127+
diffs := newDiffs()
128+
diff(diffs, "", sourceJsonNode, patchJsonNode, option)
129+
doOption(diffs, option, sourceJsonNode, patchJsonNode)
130+
return diffs.d
131131
}
132132

133133
// AsDiffs 比较 patch 相比于 source 的差别,返回 json 格式的差异文档。
134134
func AsDiffs(source, patch []byte, options ...JsonDiffOption) ([]byte, error) {
135-
sourceJsonNode, _ := Unmarshal(source)
136-
patchJsonNode, _ := Unmarshal(patch)
135+
sourceJsonNode, _ := Unmarshal(source)
136+
patchJsonNode, _ := Unmarshal(patch)
137137
dict := marshalSlice(GetDiffNode(sourceJsonNode, patchJsonNode, options...))
138138
return json.Marshal(dict)
139139
}
@@ -190,36 +190,35 @@ func merge(srcNode, diffNode *JsonNode) error {
190190
func MergeDiff(source, diff []byte) ([]byte, error) {
191191
diffNode, err := Unmarshal(diff)
192192
if err != nil {
193-
return nil, errors.Wrap(err, "fail to unmarshal diff data")
194-
}
193+
return nil, errors.Wrap(err, "fail to unmarshal diff data")
194+
}
195195
srcNode, err := Unmarshal(source)
196196
if err != nil {
197-
return nil, errors.Wrap(err, "fail to unmarshal source data")
198-
}
197+
return nil, errors.Wrap(err, "fail to unmarshal source data")
198+
}
199199
result, err := MergeDiffNode(srcNode, diffNode)
200200
if err != nil {
201-
return nil, errors.Wrap(err, "fail to merge diff")
202-
}
201+
return nil, errors.Wrap(err, "fail to merge diff")
202+
}
203203
return Marshal(result)
204204
}
205205

206206
// MergeDiffNode 将 JsonNode 类型的 diffs 应用于源 source 上,并返回合并后的新 jsonNode 对象
207207
// 如果 diffs 不合法,第二个参数将会返回 BadDiffsError
208208
func MergeDiffNode(source, diffs *JsonNode) (*JsonNode, error) {
209-
if diffs == nil {
210-
return source, nil
211-
}
212-
if diffs.Type != JsonNodeTypeSlice {
213-
return nil, errors.New("bad diffs")
214-
}
215-
copyNode := new(JsonNode)
216-
err := DeepCopy(copyNode, source)
217-
if err != nil {
218-
return nil, errors.Wrap(err, "fail to deep copy source")
219-
}
220-
err = merge(copyNode, diffs)
221-
if err != nil {
222-
return nil, errors.Wrap(err, "fail to merge")
223-
}
224-
return copyNode, nil
209+
if diffs == nil {
210+
return source, nil
211+
}
212+
if diffs.Type != JsonNodeTypeSlice {
213+
return nil, errors.New("bad diffs")
214+
}
215+
copyNode, err := DeepCopy(source)
216+
if err != nil {
217+
return nil, errors.Wrap(err, "fail to deep copy source")
218+
}
219+
err = merge(copyNode, diffs)
220+
if err != nil {
221+
return nil, errors.Wrap(err, "fail to merge")
222+
}
223+
return copyNode, nil
225224
}

jsondiff_test.go

+3-4
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,10 @@ func Test_merge_smoke(t *testing.T) {
7575
want: wantNode,
7676
}
7777
}
78-
7978
for _, cs := range testCases {
8079
t.Run(cs.name, func(t *testing.T) {
8180
src := new(JsonNode)
82-
err := DeepCopy(src, cs.args.srcNode)
81+
src, err := DeepCopy(cs.args.srcNode)
8382
if err != nil {
8483
t.Errorf("fail to deepcopy src object")
8584
}
@@ -150,8 +149,8 @@ func TestGetDiff(t *testing.T) {
150149
}
151150
for _, tt := range tss {
152151
t.Run(tt.name, func(t *testing.T) {
153-
src, _ := Unmarshal(tt.args.source)
154-
pat, _ := Unmarshal(tt.args.patch)
152+
src, _ := Unmarshal(tt.args.source)
153+
pat, _ := Unmarshal(tt.args.patch)
155154
diffs := GetDiffNode(src, pat, tt.args.options...)
156155
if !eq(diffs, tt.want) {
157156
got, _ := Marshal(diffs)

test_data/deepcopy_test.json

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
{
2+
"obj": {
3+
"obj": {
4+
"a": false,
5+
"b": 1,
6+
"c": {
7+
"cc": 1,
8+
"cd": true,
9+
"ce": [
10+
1,
11+
2,
12+
{
13+
"cea": 1,
14+
"ceb": false,
15+
"cec": [1, 2]
16+
},
17+
[3, 4, 5]
18+
]
19+
}
20+
},
21+
"slice": [
22+
1, 2, 3, 4,
23+
{
24+
"ba": 1,
25+
"bb": [
26+
12, "jjj", true, {
27+
"bba": 7
28+
}
29+
],
30+
"bc": 3,
31+
"bd": true
32+
},
33+
true, false, "fvv"
34+
],
35+
"va": 1,
36+
"va2": -0,
37+
"va3": false,
38+
"va4": true,
39+
"va5": "sds",
40+
"va6": 2e8
41+
}
42+
}

0 commit comments

Comments
 (0)