@diffson/core 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -0
- package/.turbo/turbo-test.log +96 -0
- package/.turbo/turbo-typecheck.log +2 -0
- package/dist/contract/constant/DiffConstants.d.ts +9 -0
- package/dist/contract/constant/DiffConstants.d.ts.map +1 -0
- package/dist/contract/constant/DiffConstants.js +9 -0
- package/dist/contract/constant/DiffConstants.js.map +1 -0
- package/dist/contract/constant/PresetName.d.ts +7 -0
- package/dist/contract/constant/PresetName.d.ts.map +1 -0
- package/dist/contract/constant/PresetName.js +8 -0
- package/dist/contract/constant/PresetName.js.map +1 -0
- package/dist/contract/constant/index.d.ts +3 -0
- package/dist/contract/constant/index.d.ts.map +1 -0
- package/dist/contract/constant/index.js +3 -0
- package/dist/contract/constant/index.js.map +1 -0
- package/dist/contract/index.d.ts +3 -0
- package/dist/contract/index.d.ts.map +1 -0
- package/dist/contract/index.js +3 -0
- package/dist/contract/index.js.map +1 -0
- package/dist/contract/type/ArrayComparator.d.ts +9 -0
- package/dist/contract/type/ArrayComparator.d.ts.map +1 -0
- package/dist/contract/type/ArrayComparator.js +3 -0
- package/dist/contract/type/ArrayComparator.js.map +1 -0
- package/dist/contract/type/ComparatorOrchestrator.d.ts +10 -0
- package/dist/contract/type/ComparatorOrchestrator.d.ts.map +1 -0
- package/dist/contract/type/ComparatorOrchestrator.js +3 -0
- package/dist/contract/type/ComparatorOrchestrator.js.map +1 -0
- package/dist/contract/type/DiffService.d.ts +28 -0
- package/dist/contract/type/DiffService.d.ts.map +1 -0
- package/dist/contract/type/DiffService.js +3 -0
- package/dist/contract/type/DiffService.js.map +1 -0
- package/dist/contract/type/JsonTypes.d.ts +6 -0
- package/dist/contract/type/JsonTypes.d.ts.map +1 -0
- package/dist/contract/type/JsonTypes.js +2 -0
- package/dist/contract/type/JsonTypes.js.map +1 -0
- package/dist/contract/type/NullComparator.d.ts +7 -0
- package/dist/contract/type/NullComparator.d.ts.map +1 -0
- package/dist/contract/type/NullComparator.js +3 -0
- package/dist/contract/type/NullComparator.js.map +1 -0
- package/dist/contract/type/ObjectComparator.d.ts +9 -0
- package/dist/contract/type/ObjectComparator.d.ts.map +1 -0
- package/dist/contract/type/ObjectComparator.js +3 -0
- package/dist/contract/type/ObjectComparator.js.map +1 -0
- package/dist/contract/type/OtherComparator.d.ts +8 -0
- package/dist/contract/type/OtherComparator.d.ts.map +1 -0
- package/dist/contract/type/OtherComparator.js +3 -0
- package/dist/contract/type/OtherComparator.js.map +1 -0
- package/dist/contract/type/PrimitiveComparator.d.ts +7 -0
- package/dist/contract/type/PrimitiveComparator.d.ts.map +1 -0
- package/dist/contract/type/PrimitiveComparator.js +3 -0
- package/dist/contract/type/PrimitiveComparator.js.map +1 -0
- package/dist/contract/type/Result.d.ts +8 -0
- package/dist/contract/type/Result.d.ts.map +1 -0
- package/dist/contract/type/Result.js +8 -0
- package/dist/contract/type/Result.js.map +1 -0
- package/dist/contract/type/SingleNodeDifference.d.ts +8 -0
- package/dist/contract/type/SingleNodeDifference.d.ts.map +1 -0
- package/dist/contract/type/SingleNodeDifference.js +13 -0
- package/dist/contract/type/SingleNodeDifference.js.map +1 -0
- package/dist/contract/type/index.d.ts +11 -0
- package/dist/contract/type/index.d.ts.map +1 -0
- package/dist/contract/type/index.js +11 -0
- package/dist/contract/type/index.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/service/comparator/ComparatorOrchestrator.d.ts +14 -0
- package/dist/service/comparator/ComparatorOrchestrator.d.ts.map +1 -0
- package/dist/service/comparator/ComparatorOrchestrator.js +56 -0
- package/dist/service/comparator/ComparatorOrchestrator.js.map +1 -0
- package/dist/service/comparator/array/AbstractArray.d.ts +12 -0
- package/dist/service/comparator/array/AbstractArray.d.ts.map +1 -0
- package/dist/service/comparator/array/AbstractArray.js +41 -0
- package/dist/service/comparator/array/AbstractArray.js.map +1 -0
- package/dist/service/comparator/array/SequentialArrayComparator.d.ts +10 -0
- package/dist/service/comparator/array/SequentialArrayComparator.d.ts.map +1 -0
- package/dist/service/comparator/array/SequentialArrayComparator.js +50 -0
- package/dist/service/comparator/array/SequentialArrayComparator.js.map +1 -0
- package/dist/service/comparator/array/SimilarArrayComparator.d.ts +22 -0
- package/dist/service/comparator/array/SimilarArrayComparator.d.ts.map +1 -0
- package/dist/service/comparator/array/SimilarArrayComparator.js +153 -0
- package/dist/service/comparator/array/SimilarArrayComparator.js.map +1 -0
- package/dist/service/comparator/array/index.d.ts +4 -0
- package/dist/service/comparator/array/index.d.ts.map +1 -0
- package/dist/service/comparator/array/index.js +4 -0
- package/dist/service/comparator/array/index.js.map +1 -0
- package/dist/service/comparator/index.d.ts +7 -0
- package/dist/service/comparator/index.d.ts.map +1 -0
- package/dist/service/comparator/index.js +7 -0
- package/dist/service/comparator/index.js.map +1 -0
- package/dist/service/comparator/nulls/DefaultNullComparator.d.ts +7 -0
- package/dist/service/comparator/nulls/DefaultNullComparator.d.ts.map +1 -0
- package/dist/service/comparator/nulls/DefaultNullComparator.js +7 -0
- package/dist/service/comparator/nulls/DefaultNullComparator.js.map +1 -0
- package/dist/service/comparator/nulls/index.d.ts +2 -0
- package/dist/service/comparator/nulls/index.d.ts.map +1 -0
- package/dist/service/comparator/nulls/index.js +2 -0
- package/dist/service/comparator/nulls/index.js.map +1 -0
- package/dist/service/comparator/object/AbstractObject.d.ts +17 -0
- package/dist/service/comparator/object/AbstractObject.d.ts.map +1 -0
- package/dist/service/comparator/object/AbstractObject.js +91 -0
- package/dist/service/comparator/object/AbstractObject.js.map +1 -0
- package/dist/service/comparator/object/LeftJoinObjectComparator.d.ts +9 -0
- package/dist/service/comparator/object/LeftJoinObjectComparator.d.ts.map +1 -0
- package/dist/service/comparator/object/LeftJoinObjectComparator.js +26 -0
- package/dist/service/comparator/object/LeftJoinObjectComparator.js.map +1 -0
- package/dist/service/comparator/object/UnionKeyObjectComparator.d.ts +9 -0
- package/dist/service/comparator/object/UnionKeyObjectComparator.d.ts.map +1 -0
- package/dist/service/comparator/object/UnionKeyObjectComparator.js +27 -0
- package/dist/service/comparator/object/UnionKeyObjectComparator.js.map +1 -0
- package/dist/service/comparator/object/index.d.ts +4 -0
- package/dist/service/comparator/object/index.d.ts.map +1 -0
- package/dist/service/comparator/object/index.js +4 -0
- package/dist/service/comparator/object/index.js.map +1 -0
- package/dist/service/comparator/other/DefaultOtherComparator.d.ts +7 -0
- package/dist/service/comparator/other/DefaultOtherComparator.d.ts.map +1 -0
- package/dist/service/comparator/other/DefaultOtherComparator.js +14 -0
- package/dist/service/comparator/other/DefaultOtherComparator.js.map +1 -0
- package/dist/service/comparator/other/index.d.ts +2 -0
- package/dist/service/comparator/other/index.d.ts.map +1 -0
- package/dist/service/comparator/other/index.js +2 -0
- package/dist/service/comparator/other/index.js.map +1 -0
- package/dist/service/comparator/primitive/DefaultPrimitiveComparator.d.ts +7 -0
- package/dist/service/comparator/primitive/DefaultPrimitiveComparator.d.ts.map +1 -0
- package/dist/service/comparator/primitive/DefaultPrimitiveComparator.js +17 -0
- package/dist/service/comparator/primitive/DefaultPrimitiveComparator.js.map +1 -0
- package/dist/service/comparator/primitive/index.d.ts +2 -0
- package/dist/service/comparator/primitive/index.d.ts.map +1 -0
- package/dist/service/comparator/primitive/index.js +2 -0
- package/dist/service/comparator/primitive/index.js.map +1 -0
- package/dist/service/diff/DiffContext.d.ts +14 -0
- package/dist/service/diff/DiffContext.d.ts.map +1 -0
- package/dist/service/diff/DiffContext.js +30 -0
- package/dist/service/diff/DiffContext.js.map +1 -0
- package/dist/service/diff/DiffService.d.ts +27 -0
- package/dist/service/diff/DiffService.d.ts.map +1 -0
- package/dist/service/diff/DiffService.js +185 -0
- package/dist/service/diff/DiffService.js.map +1 -0
- package/dist/service/diff/PathTracker.d.ts +22 -0
- package/dist/service/diff/PathTracker.d.ts.map +1 -0
- package/dist/service/diff/PathTracker.js +57 -0
- package/dist/service/diff/PathTracker.js.map +1 -0
- package/dist/service/diff/index.d.ts +2 -0
- package/dist/service/diff/index.d.ts.map +1 -0
- package/dist/service/diff/index.js +2 -0
- package/dist/service/diff/index.js.map +1 -0
- package/dist/service/index.d.ts +3 -0
- package/dist/service/index.d.ts.map +1 -0
- package/dist/service/index.js +3 -0
- package/dist/service/index.js.map +1 -0
- package/dist/util/TypeGuards.d.ts +7 -0
- package/dist/util/TypeGuards.d.ts.map +1 -0
- package/dist/util/TypeGuards.js +33 -0
- package/dist/util/TypeGuards.js.map +1 -0
- package/dist/util/index.d.ts +2 -0
- package/dist/util/index.d.ts.map +1 -0
- package/dist/util/index.js +2 -0
- package/dist/util/index.js.map +1 -0
- package/package.json +40 -0
- package/src/contract/constant/DiffConstants.ts +10 -0
- package/src/contract/constant/PresetName.ts +6 -0
- package/src/contract/constant/index.ts +2 -0
- package/src/contract/index.ts +2 -0
- package/src/contract/type/ArrayComparator.ts +15 -0
- package/src/contract/type/ComparatorOrchestrator.ts +16 -0
- package/src/contract/type/DiffService.ts +31 -0
- package/src/contract/type/JsonTypes.ts +3 -0
- package/src/contract/type/NullComparator.ts +9 -0
- package/src/contract/type/ObjectComparator.ts +15 -0
- package/src/contract/type/OtherComparator.ts +14 -0
- package/src/contract/type/PrimitiveComparator.ts +13 -0
- package/src/contract/type/Result.ts +7 -0
- package/src/contract/type/SingleNodeDifference.ts +13 -0
- package/src/contract/type/index.ts +10 -0
- package/src/index.ts +17 -0
- package/src/service/comparator/ComparatorOrchestrator.ts +50 -0
- package/src/service/comparator/array/AbstractArray.ts +34 -0
- package/src/service/comparator/array/SequentialArrayComparator.test.ts +46 -0
- package/src/service/comparator/array/SequentialArrayComparator.ts +48 -0
- package/src/service/comparator/array/SimilarArrayComparator.test.ts +37 -0
- package/src/service/comparator/array/SimilarArrayComparator.ts +211 -0
- package/src/service/comparator/array/index.ts +3 -0
- package/src/service/comparator/index.ts +6 -0
- package/src/service/comparator/nulls/DefaultNullComparator.test.ts +38 -0
- package/src/service/comparator/nulls/DefaultNullComparator.ts +9 -0
- package/src/service/comparator/nulls/index.ts +1 -0
- package/src/service/comparator/object/AbstractObject.ts +102 -0
- package/src/service/comparator/object/LeftJoinObjectComparator.test.ts +71 -0
- package/src/service/comparator/object/LeftJoinObjectComparator.ts +18 -0
- package/src/service/comparator/object/UnionKeyObjectComparator.test.ts +20 -0
- package/src/service/comparator/object/UnionKeyObjectComparator.ts +19 -0
- package/src/service/comparator/object/index.ts +3 -0
- package/src/service/comparator/other/DefaultOtherComparator.test.ts +48 -0
- package/src/service/comparator/other/DefaultOtherComparator.ts +23 -0
- package/src/service/comparator/other/index.ts +1 -0
- package/src/service/comparator/primitive/DefaultPrimitiveComparator.test.ts +50 -0
- package/src/service/comparator/primitive/DefaultPrimitiveComparator.ts +27 -0
- package/src/service/comparator/primitive/index.ts +1 -0
- package/src/service/diff/Diff.test.ts +113 -0
- package/src/service/diff/DiffContext.ts +37 -0
- package/src/service/diff/DiffService.test.ts +292 -0
- package/src/service/diff/DiffService.ts +245 -0
- package/src/service/diff/PathTracker.ts +71 -0
- package/src/service/diff/index.ts +1 -0
- package/src/service/index.ts +2 -0
- package/src/util/TypeGuards.test.ts +90 -0
- package/src/util/TypeGuards.ts +33 -0
- package/src/util/index.ts +1 -0
- package/tsconfig.json +11 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { describe, it, expect } from "bun:test";
|
|
2
|
+
import { DiffService } from "./DiffService";
|
|
3
|
+
|
|
4
|
+
describe("DiffService", () => {
|
|
5
|
+
describe("diffElement", () => {
|
|
6
|
+
it("should return empty array for identical objects", () => {
|
|
7
|
+
const left = { name: "test", value: 1 };
|
|
8
|
+
const right = { name: "test", value: 1 };
|
|
9
|
+
|
|
10
|
+
const diffService = new DiffService();
|
|
11
|
+
const results = diffService.diffElement(left, right);
|
|
12
|
+
|
|
13
|
+
expect(results).toEqual([]);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("should detect modified values", () => {
|
|
17
|
+
const left = { name: "test", value: 1 };
|
|
18
|
+
const right = { name: "test", value: 2 };
|
|
19
|
+
|
|
20
|
+
const diffService = new DiffService();
|
|
21
|
+
const results = diffService.diffElement(left, right);
|
|
22
|
+
|
|
23
|
+
expect(results.length).toBe(1);
|
|
24
|
+
expect(results[0].diffType).toBe("MODIFY");
|
|
25
|
+
expect(results[0].leftPath).toBe("value");
|
|
26
|
+
expect(results[0].left).toBe("1");
|
|
27
|
+
expect(results[0].right).toBe("2");
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("should detect added fields", () => {
|
|
31
|
+
const left = { name: "test" };
|
|
32
|
+
const right = { name: "test", value: 1 };
|
|
33
|
+
|
|
34
|
+
const diffService = new DiffService();
|
|
35
|
+
const results = diffService.diffElement(left, right);
|
|
36
|
+
|
|
37
|
+
expect(results.length).toBe(1);
|
|
38
|
+
expect(results[0].diffType).toBe("ADD");
|
|
39
|
+
expect(results[0].rightPath).toBe("value");
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("should detect deleted fields", () => {
|
|
43
|
+
const left = { name: "test", value: 1 };
|
|
44
|
+
const right = { name: "test" };
|
|
45
|
+
|
|
46
|
+
const diffService = new DiffService();
|
|
47
|
+
const results = diffService.diffElement(left, right);
|
|
48
|
+
|
|
49
|
+
expect(results.length).toBe(1);
|
|
50
|
+
expect(results[0].diffType).toBe("DELETE");
|
|
51
|
+
expect(results[0].leftPath).toBe("value");
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe("diffJson", () => {
|
|
56
|
+
it("should parse JSON strings and diff them", () => {
|
|
57
|
+
const leftJson = '{"name":"test","value":1}';
|
|
58
|
+
const rightJson = '{"name":"test","value":2}';
|
|
59
|
+
|
|
60
|
+
const diffService = new DiffService();
|
|
61
|
+
const results = diffService.diffJson(leftJson, rightJson);
|
|
62
|
+
|
|
63
|
+
expect(results.length).toBe(1);
|
|
64
|
+
expect(results[0].diffType).toBe("MODIFY");
|
|
65
|
+
expect(results[0].leftPath).toBe("value");
|
|
66
|
+
expect(results[0].left).toBe("1");
|
|
67
|
+
expect(results[0].right).toBe("2");
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("should return empty array for identical JSON strings", () => {
|
|
71
|
+
const leftJson = '{"name":"test","value":1}';
|
|
72
|
+
const rightJson = '{"name":"test","value":1}';
|
|
73
|
+
|
|
74
|
+
const diffService = new DiffService();
|
|
75
|
+
const results = diffService.diffJson(leftJson, rightJson);
|
|
76
|
+
|
|
77
|
+
expect(results).toEqual([]);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("should throw error for invalid left JSON string", () => {
|
|
81
|
+
const leftJson = '{invalid json}';
|
|
82
|
+
const rightJson = '{"name":"test"}';
|
|
83
|
+
|
|
84
|
+
const diffService = new DiffService();
|
|
85
|
+
|
|
86
|
+
expect(() => diffService.diffJson(leftJson, rightJson)).toThrow("Failed to parse left JSON string");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("should throw error for invalid right JSON string", () => {
|
|
90
|
+
const leftJson = '{"name":"test"}';
|
|
91
|
+
const rightJson = '{invalid json}';
|
|
92
|
+
|
|
93
|
+
const diffService = new DiffService();
|
|
94
|
+
|
|
95
|
+
expect(() => diffService.diffJson(leftJson, rightJson)).toThrow("Failed to parse right JSON string");
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
describe("nested objects", () => {
|
|
100
|
+
it("should handle nested objects", () => {
|
|
101
|
+
const left = { user: { name: "Alice", age: 30 } };
|
|
102
|
+
const right = { user: { name: "Bob", age: 30 } };
|
|
103
|
+
|
|
104
|
+
const diffService = new DiffService();
|
|
105
|
+
const results = diffService.diffElement(left, right);
|
|
106
|
+
|
|
107
|
+
expect(results.length).toBe(1);
|
|
108
|
+
expect(results[0].leftPath).toBe("user.name");
|
|
109
|
+
expect(results[0].left).toBe("Alice");
|
|
110
|
+
expect(results[0].right).toBe("Bob");
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { SingleNodeDifference } from "../../contract/type";
|
|
2
|
+
|
|
3
|
+
export class DiffContext {
|
|
4
|
+
private _isSame: boolean;
|
|
5
|
+
private singleNodeDifferences: SingleNodeDifference[];
|
|
6
|
+
private specialPathResult: string[] | null;
|
|
7
|
+
|
|
8
|
+
constructor(isSame: boolean = true) {
|
|
9
|
+
this._isSame = isSame;
|
|
10
|
+
this.singleNodeDifferences = [];
|
|
11
|
+
this.specialPathResult = null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
isSame(): boolean {
|
|
15
|
+
return this._isSame;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
setSame(same: boolean): void {
|
|
19
|
+
this._isSame = same;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
getDiffResultModels(): SingleNodeDifference[] {
|
|
23
|
+
return this.singleNodeDifferences;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
setDiffResultModels(singleNodeDifferences: SingleNodeDifference[]): void {
|
|
27
|
+
this.singleNodeDifferences = singleNodeDifferences;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
getSpecialPathResult(): string[] | null {
|
|
31
|
+
return this.specialPathResult;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
setSpecialPathResult(specialPathResult: string[]): void {
|
|
35
|
+
this.specialPathResult = specialPathResult;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import { describe, it, expect } from "bun:test";
|
|
2
|
+
import { DiffService } from "./DiffService";
|
|
3
|
+
import { LeftJoinObjectComparator, SequentialArrayComparator } from "#service";
|
|
4
|
+
import { PresetName } from "#contract";
|
|
5
|
+
|
|
6
|
+
describe("DiffService", () => {
|
|
7
|
+
describe("basic comparison", () => {
|
|
8
|
+
it("should return empty array for identical objects", () => {
|
|
9
|
+
const left = { name: "test", value: 1 };
|
|
10
|
+
const right = { name: "test", value: 1 };
|
|
11
|
+
|
|
12
|
+
const diffService = new DiffService();
|
|
13
|
+
const results = diffService.diffElement(left, right);
|
|
14
|
+
|
|
15
|
+
expect(results).toEqual([]);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("should detect modified values", () => {
|
|
19
|
+
const left = { name: "test", value: 1 };
|
|
20
|
+
const right = { name: "test", value: 2 };
|
|
21
|
+
|
|
22
|
+
const diffService = new DiffService();
|
|
23
|
+
const results = diffService.diffElement(left, right);
|
|
24
|
+
|
|
25
|
+
expect(results.length).toBe(1);
|
|
26
|
+
expect(results[0].diffType).toBe("MODIFY");
|
|
27
|
+
expect(results[0].leftPath).toBe("value");
|
|
28
|
+
expect(results[0].left).toBe("1");
|
|
29
|
+
expect(results[0].right).toBe("2");
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe("withArrayComparator", () => {
|
|
34
|
+
it("should use sequential array comparator when configured", () => {
|
|
35
|
+
const left = { items: [1, 2, 3] };
|
|
36
|
+
const right = { items: [1, 4, 3] };
|
|
37
|
+
|
|
38
|
+
const diffService = new DiffService().withArrayComparator(SequentialArrayComparator);
|
|
39
|
+
const results = diffService.diffElement(left, right);
|
|
40
|
+
console.log(results);
|
|
41
|
+
|
|
42
|
+
expect(results.length).toBe(1);
|
|
43
|
+
expect(results[0].leftPath).toBe("items.[1]");
|
|
44
|
+
expect(results[0].left).toBe("2");
|
|
45
|
+
expect(results[0].right).toBe("4");
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("should reuse injector when configuration changes", () => {
|
|
49
|
+
const diffService = new DiffService();
|
|
50
|
+
|
|
51
|
+
// First comparison with default comparator
|
|
52
|
+
const left1 = { items: [1, 2] };
|
|
53
|
+
const right1 = { items: [2, 1] };
|
|
54
|
+
const results1 = diffService.diffElement(left1, right1);
|
|
55
|
+
|
|
56
|
+
// Should match similar elements (default behavior)
|
|
57
|
+
expect(results1).toEqual([]);
|
|
58
|
+
|
|
59
|
+
// Change to sequential comparator
|
|
60
|
+
diffService.withArrayComparator(SequentialArrayComparator);
|
|
61
|
+
|
|
62
|
+
// Second comparison with sequential comparator
|
|
63
|
+
const results2 = diffService.diffElement(left1, right1);
|
|
64
|
+
|
|
65
|
+
// Should detect differences at each index
|
|
66
|
+
expect(results2.length).toBe(2);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe("preset", () => {
|
|
71
|
+
it("should use FullOrdered preset", () => {
|
|
72
|
+
const left = { items: [1, 2, 3] };
|
|
73
|
+
const right = { items: [1, 4, 3] };
|
|
74
|
+
|
|
75
|
+
const diffService = new DiffService(PresetName.FullOrdered);
|
|
76
|
+
const results = diffService.diffElement(left, right);
|
|
77
|
+
|
|
78
|
+
expect(results.length).toBe(1);
|
|
79
|
+
expect(results[0].leftPath).toBe("items.[1]");
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("should use LeftSmart preset", () => {
|
|
83
|
+
const left = { a: 1, b: 2 };
|
|
84
|
+
const right = { a: 1, b: 2, c: 3 };
|
|
85
|
+
|
|
86
|
+
const diffService = new DiffService(PresetName.LeftSmart);
|
|
87
|
+
const results = diffService.diffElement(left, right);
|
|
88
|
+
|
|
89
|
+
// LeftSmart only compares keys from left object
|
|
90
|
+
expect(results).toEqual([]);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("should use LeftOrdered preset", () => {
|
|
94
|
+
const left = { a: 1, items: [1, 2] };
|
|
95
|
+
const right = { a: 1, b: 2, items: [2, 1] };
|
|
96
|
+
|
|
97
|
+
const diffService = new DiffService(PresetName.LeftOrdered);
|
|
98
|
+
const results = diffService.diffElement(left, right);
|
|
99
|
+
|
|
100
|
+
// Should use leftJoin for objects and sequential for arrays
|
|
101
|
+
expect(results.length).toBe(2); // Two array differences
|
|
102
|
+
expect(results[0].leftPath).toBe("items.[0]");
|
|
103
|
+
expect(results[1].leftPath).toBe("items.[1]");
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe("compareWithOptions", () => {
|
|
108
|
+
it("should ignore noise paths", () => {
|
|
109
|
+
const left = { name: "test", timestamp: 1000 };
|
|
110
|
+
const right = { name: "test", timestamp: 2000 };
|
|
111
|
+
|
|
112
|
+
const diffService = new DiffService();
|
|
113
|
+
const results = diffService.diffElement(left, right, {
|
|
114
|
+
noisePath: ["timestamp"],
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
expect(results).toEqual([]);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("should handle special paths", () => {
|
|
121
|
+
const left = { name: "test", special: "value1" };
|
|
122
|
+
const right = { name: "test", special: "value2" };
|
|
123
|
+
|
|
124
|
+
const diffService = new DiffService();
|
|
125
|
+
const results = diffService.diffElement(left, right, {
|
|
126
|
+
specialPath: ["special"],
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// Special paths are tracked differently
|
|
130
|
+
expect(results.length).toBe(1);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
describe("fluent API", () => {
|
|
135
|
+
it("should support method chaining", () => {
|
|
136
|
+
const diffService = new DiffService()
|
|
137
|
+
.withArrayComparator(SequentialArrayComparator)
|
|
138
|
+
.withObjectComparator(LeftJoinObjectComparator);
|
|
139
|
+
|
|
140
|
+
const left = { a: 1, items: [1, 2] };
|
|
141
|
+
const right = { a: 1, b: 2, items: [2, 1] };
|
|
142
|
+
|
|
143
|
+
const results = diffService.diffElement(left, right);
|
|
144
|
+
|
|
145
|
+
// Should use both custom comparators
|
|
146
|
+
expect(results.length).toBeGreaterThan(0);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
describe("DiffService - parseNestedJson", () => {
|
|
152
|
+
it("should parse nested JSON strings when parseNestedJson option is true", () => {
|
|
153
|
+
const leftJson = JSON.stringify({
|
|
154
|
+
user: '{"name": "Alice", "age": 30}',
|
|
155
|
+
settings: '{"theme": "dark", "notifications": true}'
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
const rightJson = JSON.stringify({
|
|
159
|
+
user: '{"name": "Bob", "age": 25}',
|
|
160
|
+
settings: '{"theme": "light", "notifications": false}'
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
const diffService = new DiffService();
|
|
164
|
+
const results = diffService.diffJson(leftJson, rightJson, {
|
|
165
|
+
parseNestedJson: true
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// 应该能检测到嵌套对象内部的差异
|
|
169
|
+
expect(results.length).toBeGreaterThan(0);
|
|
170
|
+
|
|
171
|
+
// 验证路径能正确指向嵌套对象的属性
|
|
172
|
+
const nameDiff = results.find(r => r.leftPath === "user.name");
|
|
173
|
+
expect(nameDiff).toBeDefined();
|
|
174
|
+
expect(nameDiff?.left).toBe("Alice");
|
|
175
|
+
expect(nameDiff?.right).toBe("Bob");
|
|
176
|
+
|
|
177
|
+
const themeDiff = results.find(r => r.leftPath === "settings.theme");
|
|
178
|
+
expect(themeDiff).toBeDefined();
|
|
179
|
+
expect(themeDiff?.left).toBe("dark");
|
|
180
|
+
expect(themeDiff?.right).toBe("light");
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it("should not parse nested JSON strings when parseNestedJson option is false or undefined", () => {
|
|
184
|
+
const leftJson = JSON.stringify({
|
|
185
|
+
user: '{"name": "Alice", "age": 30}'
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
const rightJson = JSON.stringify({
|
|
189
|
+
user: '{"name": "Bob", "age": 25}'
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
const diffService = new DiffService();
|
|
193
|
+
const results = diffService.diffJson(leftJson, rightJson);
|
|
194
|
+
|
|
195
|
+
// 不应该解析嵌套 JSON,应该把整个字符串当作一个值
|
|
196
|
+
expect(results.length).toBe(1);
|
|
197
|
+
expect(results[0].leftPath).toBe("user");
|
|
198
|
+
expect(results[0].left).toBe('{"name": "Alice", "age": 30}');
|
|
199
|
+
expect(results[0].right).toBe('{"name": "Bob", "age": 25}');
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it("should handle deeply nested JSON strings", () => {
|
|
203
|
+
const leftJson = JSON.stringify({
|
|
204
|
+
data: '{"level1": {"level2": "value1"}}'
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
const rightJson = JSON.stringify({
|
|
208
|
+
data: '{"level1": {"level2": "value2"}}'
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
const diffService = new DiffService();
|
|
212
|
+
const results = diffService.diffJson(leftJson, rightJson, {
|
|
213
|
+
parseNestedJson: true
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
// 应该能检测到多层嵌套的差异
|
|
217
|
+
const deepDiff = results.find(r => r.leftPath === "data.level1.level2");
|
|
218
|
+
expect(deepDiff).toBeDefined();
|
|
219
|
+
expect(deepDiff?.left).toBe("value1");
|
|
220
|
+
expect(deepDiff?.right).toBe("value2");
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it("should handle arrays containing JSON strings", () => {
|
|
224
|
+
const leftJson = JSON.stringify({
|
|
225
|
+
items: ['{"id": 1, "name": "Item1"}', '{"id": 2, "name": "Item2"}']
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
const rightJson = JSON.stringify({
|
|
229
|
+
items: ['{"id": 1, "name": "Item1"}', '{"id": 2, "name": "Item2Modified"}']
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
const diffService = new DiffService();
|
|
233
|
+
const results = diffService.diffJson(leftJson, rightJson, {
|
|
234
|
+
parseNestedJson: true
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
// 应该能检测到数组元素中嵌套对象的差异
|
|
238
|
+
const itemDiff = results.find(r => r.leftPath === "items.[1].name");
|
|
239
|
+
expect(itemDiff).toBeDefined();
|
|
240
|
+
expect(itemDiff?.left).toBe("Item2");
|
|
241
|
+
expect(itemDiff?.right).toBe("Item2Modified");
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it("should handle mixed content with both JSON strings and regular strings", () => {
|
|
245
|
+
const leftJson = JSON.stringify({
|
|
246
|
+
regularString: "Hello",
|
|
247
|
+
jsonString: '{"key": "value1"}',
|
|
248
|
+
number: 42
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
const rightJson = JSON.stringify({
|
|
252
|
+
regularString: "World",
|
|
253
|
+
jsonString: '{"key": "value2"}',
|
|
254
|
+
number: 42
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
const diffService = new DiffService(PresetName.LeftOrdered);
|
|
258
|
+
const results = diffService.diffJson(leftJson, rightJson, {
|
|
259
|
+
parseNestedJson: true
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
expect(results.length).toBe(2);
|
|
263
|
+
|
|
264
|
+
const regularDiff = results.find(r => r.leftPath === "regularString");
|
|
265
|
+
expect(regularDiff?.left).toBe("Hello");
|
|
266
|
+
expect(regularDiff?.right).toBe("World");
|
|
267
|
+
|
|
268
|
+
const jsonDiff = results.find(r => r.leftPath === "jsonString.key");
|
|
269
|
+
expect(jsonDiff?.left).toBe("value1");
|
|
270
|
+
expect(jsonDiff?.right).toBe("value2");
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
it("should not fail on invalid JSON strings", () => {
|
|
274
|
+
const leftJson = JSON.stringify({
|
|
275
|
+
invalid: '{"not valid json'
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
const rightJson = JSON.stringify({
|
|
279
|
+
invalid: '{"not valid json'
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
const diffService = new DiffService();
|
|
283
|
+
const results = diffService.diffJson(leftJson, rightJson, {
|
|
284
|
+
parseNestedJson: true
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
// 无效的 JSON 字符串应该保持原样
|
|
288
|
+
expect(results.length).toBe(0);
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import isJSON from "is-json";
|
|
2
|
+
import { Injector } from "@wendellhu/redi";
|
|
3
|
+
import {
|
|
4
|
+
type JsonValue,
|
|
5
|
+
Result,
|
|
6
|
+
SingleNodeDifference,
|
|
7
|
+
type IDiffService,
|
|
8
|
+
IComparatorOrchestrator,
|
|
9
|
+
IObjectComparator,
|
|
10
|
+
IArrayComparator,
|
|
11
|
+
IPrimitiveComparator,
|
|
12
|
+
INullComparator,
|
|
13
|
+
IOtherComparator,
|
|
14
|
+
type IComparatorOrchestrator as IComparatorOrchestratorType,
|
|
15
|
+
PresetName,
|
|
16
|
+
} from "#contract";
|
|
17
|
+
import { SPLIT_PATH, OBJECT_NULL, TYPE_MODIFY, TYPE_ADD, TYPE_DELETE } from "#contract";
|
|
18
|
+
import { DiffContext } from "./DiffContext";
|
|
19
|
+
import { PathTracker } from "./PathTracker";
|
|
20
|
+
import { ComparatorOrchestrator } from "../comparator/ComparatorOrchestrator";
|
|
21
|
+
import { UnionKeyObjectComparator } from "../comparator/object/UnionKeyObjectComparator";
|
|
22
|
+
import { LeftJoinObjectComparator } from "../comparator/object/LeftJoinObjectComparator";
|
|
23
|
+
import { SimilarArrayComparator } from "../comparator/array/SimilarArrayComparator";
|
|
24
|
+
import { SequentialArrayComparator } from "../comparator/array/SequentialArrayComparator";
|
|
25
|
+
import { DefaultPrimitiveComparator } from "../comparator/primitive/DefaultPrimitiveComparator";
|
|
26
|
+
import { DefaultNullComparator } from "../comparator/nulls/DefaultNullComparator";
|
|
27
|
+
import { DefaultOtherComparator } from "../comparator/other/DefaultOtherComparator";
|
|
28
|
+
|
|
29
|
+
export class DiffService implements IDiffService {
|
|
30
|
+
private config: {
|
|
31
|
+
objectComparator: new (orchestrator: IComparatorOrchestrator) => IObjectComparator;
|
|
32
|
+
arrayComparator: new (orchestrator: IComparatorOrchestrator) => IArrayComparator;
|
|
33
|
+
primitiveComparator: new () => IPrimitiveComparator;
|
|
34
|
+
nullComparator: new () => INullComparator;
|
|
35
|
+
otherComparator: new () => IOtherComparator;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
constructor(preset: PresetName = PresetName.FullSmart) {
|
|
39
|
+
this.config = {
|
|
40
|
+
objectComparator: UnionKeyObjectComparator,
|
|
41
|
+
arrayComparator: SimilarArrayComparator,
|
|
42
|
+
primitiveComparator: DefaultPrimitiveComparator,
|
|
43
|
+
nullComparator: DefaultNullComparator,
|
|
44
|
+
otherComparator: DefaultOtherComparator,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
switch (preset) {
|
|
48
|
+
case PresetName.FullOrdered:
|
|
49
|
+
this.config.arrayComparator = SequentialArrayComparator;
|
|
50
|
+
break;
|
|
51
|
+
case PresetName.LeftSmart:
|
|
52
|
+
this.config.objectComparator = LeftJoinObjectComparator;
|
|
53
|
+
break;
|
|
54
|
+
case PresetName.LeftOrdered:
|
|
55
|
+
this.config.objectComparator = LeftJoinObjectComparator;
|
|
56
|
+
this.config.arrayComparator = SequentialArrayComparator;
|
|
57
|
+
break;
|
|
58
|
+
case PresetName.FullSmart:
|
|
59
|
+
default:
|
|
60
|
+
this.config.objectComparator = UnionKeyObjectComparator;
|
|
61
|
+
this.config.arrayComparator = SimilarArrayComparator;
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
withObjectComparator(cls: new (orchestrator: IComparatorOrchestrator) => IObjectComparator): this {
|
|
67
|
+
this.config.objectComparator = cls;
|
|
68
|
+
return this;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
withArrayComparator(cls: new (orchestrator: IComparatorOrchestrator) => IArrayComparator): this {
|
|
72
|
+
this.config.arrayComparator = cls;
|
|
73
|
+
return this;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
withPrimitiveComparator(cls: new () => IPrimitiveComparator): this {
|
|
77
|
+
this.config.primitiveComparator = cls;
|
|
78
|
+
return this;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
withNullComparator(cls: new () => INullComparator): this {
|
|
82
|
+
this.config.nullComparator = cls;
|
|
83
|
+
return this;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
withOtherComparator(cls: new () => IOtherComparator): this {
|
|
87
|
+
this.config.otherComparator = cls;
|
|
88
|
+
return this;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
diffJson(leftJson: string, rightJson: string, options?: {
|
|
92
|
+
noisePath?: string[];
|
|
93
|
+
specialPath?: string[];
|
|
94
|
+
parseNestedJson?: boolean;
|
|
95
|
+
}): Result[] {
|
|
96
|
+
let left: JsonValue;
|
|
97
|
+
let right: JsonValue;
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
left = JSON.parse(leftJson);
|
|
101
|
+
} catch (error) {
|
|
102
|
+
throw new Error(`Failed to parse left JSON string: ${error instanceof Error ? error.message : String(error)}`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
right = JSON.parse(rightJson);
|
|
107
|
+
} catch (error) {
|
|
108
|
+
throw new Error(`Failed to parse right JSON string: ${error instanceof Error ? error.message : String(error)}`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// 将解析后的对象和选项传递给 diffElement
|
|
112
|
+
return this.diffElement(left, right, options);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
diffElement(
|
|
116
|
+
left: JsonValue,
|
|
117
|
+
right: JsonValue,
|
|
118
|
+
options?: {
|
|
119
|
+
noisePath?: string[];
|
|
120
|
+
specialPath?: string[];
|
|
121
|
+
parseNestedJson?: boolean;
|
|
122
|
+
}
|
|
123
|
+
): Result[] {
|
|
124
|
+
// 如果启用递归解析嵌套 JSON
|
|
125
|
+
if (options?.parseNestedJson) {
|
|
126
|
+
left = this.parseNestedJsonStrings(left);
|
|
127
|
+
right = this.parseNestedJsonStrings(right);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const orchestrator = this.getOrCreateOrchestrator();
|
|
131
|
+
const diffContext = this.compareInternal(left, right, orchestrator, options);
|
|
132
|
+
return this.constructResult(diffContext);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
private getOrCreateOrchestrator(): IComparatorOrchestratorType {
|
|
136
|
+
const injector = new Injector([
|
|
137
|
+
[IComparatorOrchestrator, { useClass: ComparatorOrchestrator, lazy: true }],
|
|
138
|
+
[IObjectComparator, { useClass: this.config.objectComparator }],
|
|
139
|
+
[IArrayComparator, { useClass: this.config.arrayComparator }],
|
|
140
|
+
[IPrimitiveComparator, { useClass: this.config.primitiveComparator }],
|
|
141
|
+
[INullComparator, { useClass: this.config.nullComparator }],
|
|
142
|
+
[IOtherComparator, { useClass: this.config.otherComparator }],
|
|
143
|
+
]);
|
|
144
|
+
return injector.get(IComparatorOrchestrator);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
private constructResult(diffContext: DiffContext): Result[] {
|
|
148
|
+
const list: Result[] = [];
|
|
149
|
+
|
|
150
|
+
for (const resultModel of diffContext.getDiffResultModels()) {
|
|
151
|
+
const printModel = this.convert(resultModel);
|
|
152
|
+
const leftAndRightBothNull =
|
|
153
|
+
resultModel.left === OBJECT_NULL && resultModel.right === OBJECT_NULL;
|
|
154
|
+
|
|
155
|
+
if (leftAndRightBothNull) {
|
|
156
|
+
printModel.diffType = TYPE_MODIFY;
|
|
157
|
+
} else if (resultModel.left === OBJECT_NULL) {
|
|
158
|
+
printModel.diffType = TYPE_ADD;
|
|
159
|
+
printModel.leftPath = null;
|
|
160
|
+
} else if (resultModel.right === OBJECT_NULL) {
|
|
161
|
+
printModel.diffType = TYPE_DELETE;
|
|
162
|
+
printModel.rightPath = null;
|
|
163
|
+
} else {
|
|
164
|
+
printModel.diffType = TYPE_MODIFY;
|
|
165
|
+
}
|
|
166
|
+
list.push(printModel);
|
|
167
|
+
}
|
|
168
|
+
return list;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
private convert(resultModel: SingleNodeDifference): Result {
|
|
172
|
+
const printModel = new Result();
|
|
173
|
+
printModel.left = resultModel.left;
|
|
174
|
+
printModel.right = resultModel.right;
|
|
175
|
+
printModel.leftPath = resultModel.leftPath;
|
|
176
|
+
printModel.rightPath = resultModel.rightPath;
|
|
177
|
+
return printModel;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
private compareInternal(
|
|
181
|
+
left: JsonValue,
|
|
182
|
+
right: JsonValue,
|
|
183
|
+
orchestrator: IComparatorOrchestratorType,
|
|
184
|
+
options?: {
|
|
185
|
+
noisePath?: string[];
|
|
186
|
+
specialPath?: string[];
|
|
187
|
+
}
|
|
188
|
+
): DiffContext {
|
|
189
|
+
const pathTracker = new PathTracker(null, null);
|
|
190
|
+
|
|
191
|
+
if (options?.noisePath) {
|
|
192
|
+
pathTracker.setNoisePahList(this.splitPath(options.noisePath));
|
|
193
|
+
}
|
|
194
|
+
if (options?.specialPath) {
|
|
195
|
+
pathTracker.setSpecialPath(this.splitPath(options.specialPath));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return orchestrator.diffElement(left, right, pathTracker);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
private splitPath(pathList: string[]): string[] {
|
|
202
|
+
const result: string[] = [];
|
|
203
|
+
for (const path of pathList) {
|
|
204
|
+
const parts = path.split(SPLIT_PATH);
|
|
205
|
+
result.push(parts.join("."));
|
|
206
|
+
}
|
|
207
|
+
return result;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
private parseNestedJsonStrings(value: JsonValue): JsonValue {
|
|
211
|
+
// 如果是字符串,尝试解析为 JSON
|
|
212
|
+
if (typeof value === "string") {
|
|
213
|
+
if (isJSON(value)) {
|
|
214
|
+
try {
|
|
215
|
+
const parsed = JSON.parse(value);
|
|
216
|
+
// 递归处理解析后的值
|
|
217
|
+
return this.parseNestedJsonStrings(parsed);
|
|
218
|
+
} catch {
|
|
219
|
+
// 如果解析失败,返回原字符串
|
|
220
|
+
return value;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return value;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// 如果是数组,递归处理每个元素
|
|
227
|
+
if (Array.isArray(value)) {
|
|
228
|
+
return value.map(item => this.parseNestedJsonStrings(item));
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// 如果是对象,递归处理每个属性值
|
|
232
|
+
if (typeof value === "object" && value !== null) {
|
|
233
|
+
const result: Record<string, JsonValue> = {};
|
|
234
|
+
for (const key in value) {
|
|
235
|
+
if (Object.prototype.hasOwnProperty.call(value, key)) {
|
|
236
|
+
result[key] = this.parseNestedJsonStrings(value[key]);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return result;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// 其他类型(number, boolean, null)直接返回
|
|
243
|
+
return value;
|
|
244
|
+
}
|
|
245
|
+
}
|