Skip to content

Commit c31db00

Browse files
committed
feat: allow ignore properties
1 parent 161e98b commit c31db00

15 files changed

Lines changed: 274 additions & 43 deletions

src/main/java/com/deblock/jsondiff/matcher/CompositeJsonMatcher.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@
22

33
import com.deblock.jsondiff.diff.*;
44
import tools.jackson.databind.JsonNode;
5-
import tools.jackson.databind.node.ArrayNode;
6-
import tools.jackson.databind.node.ObjectNode;
7-
import tools.jackson.databind.node.ValueNode;
85

96
import java.util.ArrayList;
107
import java.util.Arrays;
@@ -21,7 +18,7 @@ public CompositeJsonMatcher(PartialJsonMatcher<?> ...jsonArrayPartialMatcher) {
2118
@Override
2219
public JsonDiff diff(Path path, JsonNode expected, JsonNode received) {
2320
return this.matchers.stream()
24-
.filter(matcher -> matcher.manage(expected, received))
21+
.filter(matcher -> matcher.manage(path, received, expected))
2522
.findFirst()
2623
.map(matcher -> matcher.jsonDiff(path, expected, received, this))
2724
.orElseGet(() -> new UnMatchedPrimaryDiff(path, expected, received));
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.deblock.jsondiff.matcher;
2+
3+
import com.deblock.jsondiff.diff.JsonDiff;
4+
import com.deblock.jsondiff.diff.MatchedPrimaryDiff;
5+
import tools.jackson.databind.JsonNode;
6+
7+
import java.util.Arrays;
8+
import java.util.List;
9+
import java.util.regex.Pattern;
10+
11+
public class IgnoringFieldMatcher implements PartialJsonMatcher {
12+
private final List<Pattern> fieldsToIgnore;
13+
14+
public IgnoringFieldMatcher(List<Pattern> patterns) {
15+
this.fieldsToIgnore = patterns;
16+
}
17+
18+
public IgnoringFieldMatcher(Pattern... patterns) {
19+
this.fieldsToIgnore = Arrays.stream(patterns).toList();
20+
}
21+
22+
@Override
23+
public JsonDiff jsonDiff(Path path, JsonNode expectedJson, JsonNode receivedJson, JsonMatcher jsonMatcher) {
24+
return new MatchedPrimaryDiff(path, expectedJson);
25+
}
26+
27+
@Override
28+
public boolean manage(Path path, JsonNode expected, JsonNode received) {
29+
String stringPath = path.toString();
30+
return fieldsToIgnore.stream().anyMatch(pattern -> pattern.matcher(stringPath).matches());
31+
}
32+
}

src/main/java/com/deblock/jsondiff/matcher/LenientJsonArrayPartialMatcher.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public JsonDiff jsonDiff(Path path, ArrayNode expectedArrayNode, ArrayNode recie
5858
}
5959

6060
@Override
61-
public boolean manage(JsonNode expected, JsonNode received) {
61+
public boolean manage(Path path, JsonNode received, JsonNode expected) {
6262
return expected.isArray() && received.isArray();
6363
}
6464

src/main/java/com/deblock/jsondiff/matcher/LenientJsonObjectPartialMatcher.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public JsonDiff jsonDiff(Path path, ObjectNode expectedJson, ObjectNode received
2929
}
3030

3131
@Override
32-
public boolean manage(JsonNode expected, JsonNode received) {
32+
public boolean manage(Path path, JsonNode received, JsonNode expected) {
3333
return expected.isObject() && received.isObject();
3434
}
3535
}

src/main/java/com/deblock/jsondiff/matcher/LenientNumberPrimitivePartialMatcher.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public JsonDiff jsonDiff(Path path, ValueNode expectedValue, ValueNode receivedV
3535
}
3636

3737
@Override
38-
public boolean manage(JsonNode expected, JsonNode received) {
38+
public boolean manage(Path path, JsonNode received, JsonNode expected) {
3939
return expected.isNumber() && received.isNumber();
4040
}
4141
}

src/main/java/com/deblock/jsondiff/matcher/NullEqualsEmptyArrayMatcher.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public JsonDiff jsonDiff(Path path, JsonNode expectedJson, JsonNode receivedJson
1919
}
2020

2121
@Override
22-
public boolean manage(JsonNode expected, JsonNode received) {
22+
public boolean manage(Path path, JsonNode received, JsonNode expected) {
2323
return (expected.isNull() && received.isArray())
2424
|| (received.isNull() && expected.isArray());
2525
}

src/main/java/com/deblock/jsondiff/matcher/PartialJsonMatcher.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@
66
public interface PartialJsonMatcher<T extends JsonNode> {
77
JsonDiff jsonDiff(Path path, T expectedJson, T receivedJson, JsonMatcher jsonMatcher);
88

9-
boolean manage(JsonNode expected, JsonNode received);
9+
boolean manage(Path path, JsonNode received, JsonNode expected);
1010

1111
}
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
package com.deblock.jsondiff.matcher;
2+
3+
4+
public class PathMatcher {
5+
public final PathMatcher.PathMatcherItem property;
6+
public final PathMatcher next;
7+
8+
public static PathMatcher from(String path) {
9+
PathMatcher matcher = new PathMatcher();
10+
for (String part: path.split("\\.")) {
11+
if (part.endsWith("]")) {
12+
String index = part.substring(part.lastIndexOf("[") + 1, part.length() - 1);
13+
matcher = matcher.add(new PathMatcherItem.ObjectProperty(part.substring(0, part.lastIndexOf("["))));
14+
if ("*".equals(index)) {
15+
matcher = matcher.add(new PathMatcherItem.WilcardMatcherItem(Path.PathItem.ArrayIndex.class));
16+
} else {
17+
matcher = matcher.add(new PathMatcherItem.ArrayIndex(Integer.parseInt(index)));
18+
}
19+
} else if ("*".equals(part)) {
20+
matcher = matcher.add(new PathMatcherItem.WilcardMatcherItem(Path.PathItem.ObjectProperty.class));
21+
} else {
22+
matcher = matcher.add(new PathMatcher.PathMatcherItem.ObjectProperty(part));
23+
}
24+
}
25+
return matcher;
26+
}
27+
28+
public PathMatcher() {
29+
this(null, null);
30+
}
31+
32+
private PathMatcher(PathMatcher.PathMatcherItem property, PathMatcher next) {
33+
this.property = property;
34+
this.next = next;
35+
}
36+
37+
private PathMatcher(PathMatcher.PathMatcherItem property) {
38+
this.property = property;
39+
this.next = null;
40+
}
41+
42+
public PathMatcher add(PathMatcher.PathMatcherItem item) {
43+
if (this.next == null) {
44+
return new PathMatcher(this.property, new PathMatcher(item));
45+
} else {
46+
return new PathMatcher(this.property, this.next.add(item));
47+
}
48+
}
49+
50+
public boolean match(Path path) {
51+
int pathLength = length(path);
52+
int matcherLength = length();
53+
54+
if (matcherLength > pathLength) {
55+
return false;
56+
}
57+
58+
// Align path to match from the end
59+
Path alignedPath = path;
60+
for (int i = 0; i < pathLength - matcherLength; i++) {
61+
alignedPath = alignedPath.next;
62+
}
63+
64+
return matchFromHere(alignedPath);
65+
}
66+
67+
private int length() {
68+
int count = 0;
69+
PathMatcher current = this;
70+
while (current != null && current.property != null) {
71+
count++;
72+
current = current.next;
73+
}
74+
return count;
75+
}
76+
77+
private int length(Path path) {
78+
int count = 0;
79+
Path current = path;
80+
while (current != null && current.property != null) {
81+
count++;
82+
current = current.next;
83+
}
84+
return count;
85+
}
86+
87+
private boolean matchFromHere(Path path) {
88+
if (this.property == null) {
89+
return next == null || next.matchFromHere(path);
90+
}
91+
if (path == null || path.property == null) {
92+
return false;
93+
}
94+
if (this.property.match(path.property)) {
95+
if (next == null) {
96+
return path.next == null || path.next.property == null;
97+
}
98+
return path.next != null && next.matchFromHere(path.next);
99+
}
100+
return false;
101+
}
102+
103+
public String toString() {
104+
return ((this.property == null) ? "$" : this.property) +
105+
((this.next == null) ? "" : "." + this.next);
106+
}
107+
108+
private interface PathMatcherItem {
109+
static PathMatcherItem of(String property) {
110+
return new PathMatcher.PathMatcherItem.ObjectProperty(property);
111+
}
112+
113+
static PathMatcherItem of(Integer index) {
114+
return new PathMatcher.PathMatcherItem.ArrayIndex(index);
115+
}
116+
117+
boolean match(Path.PathItem pathItem);
118+
119+
class WilcardMatcherItem implements PathMatcherItem {
120+
private final Class<? extends Path.PathItem> type;
121+
122+
public WilcardMatcherItem(Class<? extends Path.PathItem> type) {
123+
this.type = type;
124+
}
125+
126+
public String toString() {
127+
return "*";
128+
}
129+
130+
@Override
131+
public boolean match(Path.PathItem pathItem) {
132+
return type.isAssignableFrom(pathItem.getClass());
133+
}
134+
}
135+
136+
class ArrayIndex implements PathMatcherItem {
137+
public final int index;
138+
139+
public ArrayIndex(int index) {
140+
this.index = index;
141+
}
142+
143+
@Override
144+
public String toString() {
145+
return String.valueOf(index);
146+
}
147+
148+
@Override
149+
public boolean match(Path.PathItem pathItem) {
150+
if (pathItem instanceof Path.PathItem.ArrayIndex arrayIndex) {
151+
return arrayIndex.index == this.index;
152+
}
153+
return false;
154+
}
155+
}
156+
157+
class ObjectProperty implements PathMatcherItem {
158+
public final String property;
159+
160+
public ObjectProperty(String property) {
161+
this.property = property;
162+
}
163+
164+
@Override
165+
public String toString() {
166+
return this.property;
167+
}
168+
169+
@Override
170+
public boolean match(Path.PathItem pathItem) {
171+
if (pathItem instanceof Path.PathItem.ObjectProperty objectProperty) {
172+
return objectProperty.property.equals(this.property);
173+
}
174+
return false;
175+
}
176+
}
177+
}
178+
}

src/main/java/com/deblock/jsondiff/matcher/StrictJsonArrayPartialMatcher.java

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,6 @@
55
import tools.jackson.databind.JsonNode;
66
import tools.jackson.databind.node.ArrayNode;
77

8-
import java.util.Comparator;
9-
import java.util.HashMap;
10-
import java.util.HashSet;
11-
import java.util.Map;
12-
import java.util.stream.Collectors;
13-
import java.util.stream.IntStream;
14-
158
public class StrictJsonArrayPartialMatcher implements PartialJsonMatcher<ArrayNode> {
169
@Override
1710
public JsonDiff jsonDiff(Path path, ArrayNode expectedValues, ArrayNode receivedValues, JsonMatcher jsonMatcher) {
@@ -38,7 +31,7 @@ public JsonDiff jsonDiff(Path path, ArrayNode expectedValues, ArrayNode received
3831
}
3932

4033
@Override
41-
public boolean manage(JsonNode expected, JsonNode received) {
34+
public boolean manage(Path path, JsonNode received, JsonNode expected) {
4235
return expected.isArray() && received.isArray();
4336
}
4437
}

src/main/java/com/deblock/jsondiff/matcher/StrictJsonObjectPartialMatcher.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public JsonDiff jsonDiff(Path path, ObjectNode expectedJson, ObjectNode received
4040
}
4141

4242
@Override
43-
public boolean manage(JsonNode expected, JsonNode received) {
43+
public boolean manage(Path path, JsonNode received, JsonNode expected) {
4444
return expected.isObject() && received.isObject();
4545
}
4646
}

0 commit comments

Comments
 (0)