Skip to content

Commit 6a8bf59

Browse files
authored
Merge pull request #54 from srfarley/collect-nested-causes
Add ValidationException#getAllMessages() for collecting nested error messages
2 parents d5847b0 + 0a9618a commit 6a8bf59

File tree

4 files changed

+113
-4
lines changed

4 files changed

+113
-4
lines changed

core/src/main/java/org/everit/json/schema/ValidationException.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import java.util.ArrayList;
2222
import java.util.Collections;
2323
import java.util.List;
24-
import java.util.Objects;
2524
import java.util.stream.Collectors;
2625

2726
import static java.util.Objects.requireNonNull;
@@ -37,6 +36,18 @@ private static int getViolationCount(final List<ValidationException> causes) {
3736
return Math.max(1, causeCount);
3837
}
3938

39+
private static List<String> getAllMessages(final List<ValidationException> causes) {
40+
List<String> messages = causes.stream()
41+
.filter(cause -> cause.causingExceptions.isEmpty())
42+
.map(ValidationException::getMessage)
43+
.collect(Collectors.toList());
44+
messages.addAll(causes.stream()
45+
.filter(cause -> !cause.causingExceptions.isEmpty())
46+
.flatMap(cause -> getAllMessages(cause.getCausingExceptions()).stream())
47+
.collect(Collectors.toList()));
48+
return messages;
49+
}
50+
4051
/**
4152
* Sort of static factory method. It is used by {@link ObjectSchema} and {@link ArraySchema} to
4253
* create {@code ValidationException}s, handling the case of multiple violations occuring during
@@ -241,6 +252,18 @@ public List<ValidationException> getCausingExceptions() {
241252
return causingExceptions;
242253
}
243254

255+
/**
256+
* Returns all messages collected from all violations, including nested causing exceptions.
257+
* @return all messages
258+
*/
259+
public List<String> getAllMessages() {
260+
if (causingExceptions.isEmpty()) {
261+
return Collections.singletonList(getMessage());
262+
} else {
263+
return getAllMessages(causingExceptions).stream().collect(Collectors.toList());
264+
}
265+
}
266+
244267
/**
245268
* Returns a programmer-readable error description prepended by {@link #getPointerToViolation()
246269
* the pointer to the violating fragment} of the JSON document.

core/src/test/java/org/everit/json/schema/ObjectSchemaTest.java

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,12 @@
1919
import nl.jqno.equalsverifier.Warning;
2020
import org.everit.json.schema.loader.SchemaLoader;
2121
import org.json.JSONObject;
22-
import org.json.JSONTokener;
2322
import org.junit.Assert;
24-
import org.junit.Ignore;
2523
import org.junit.Test;
2624

25+
import java.util.List;
26+
import java.util.concurrent.Callable;
27+
2728
import static org.junit.Assert.assertTrue;
2829

2930
public class ObjectSchemaTest {
@@ -136,9 +137,74 @@ public void multipleViolations() {
136137
Assert.fail("did not throw exception for 3 schema violations");
137138
} catch (ValidationException e) {
138139
Assert.assertEquals(3, e.getCausingExceptions().size());
139-
Assert.assertEquals(1, TestSupport.countCauseByJsonPointer(e, "#/numberProp"));
140140
Assert.assertEquals(1, TestSupport.countCauseByJsonPointer(e, "#"));
141+
Assert.assertEquals(1, TestSupport.countCauseByJsonPointer(e, "#/numberProp"));
141142
Assert.assertEquals(1, TestSupport.countCauseByJsonPointer(e, "#/stringPatternMatch"));
143+
144+
List<String> messages = e.getAllMessages();
145+
Assert.assertEquals(3, messages.size());
146+
Assert.assertEquals(1, TestSupport.countMatchingMessage(messages, "#:"));
147+
Assert.assertEquals(1, TestSupport.countMatchingMessage(messages, "#/numberProp:"));
148+
Assert.assertEquals(1, TestSupport.countMatchingMessage(messages, "#/stringPatternMatch:"));
149+
}
150+
}
151+
152+
@SuppressWarnings("OptionalGetWithoutIsPresent")
153+
@Test
154+
public void multipleViolationsNested() throws Exception {
155+
Callable<ObjectSchema.Builder> newBuilder = () -> ObjectSchema.builder()
156+
.addPropertySchema("numberProp", new NumberSchema())
157+
.patternProperty("^string.*", new StringSchema())
158+
.addPropertySchema("boolProp", BooleanSchema.INSTANCE)
159+
.addRequiredProperty("boolProp");
160+
161+
Schema nested2 = newBuilder.call().build();
162+
Schema nested1 = newBuilder.call().addPropertySchema("nested", nested2).build();
163+
Schema subject = newBuilder.call().addPropertySchema("nested", nested1).build();
164+
165+
try {
166+
subject.validate(OBJECTS.get("multipleViolationsNested"));
167+
Assert.fail("did not throw exception for 9 schema violations");
168+
} catch (ValidationException subjectException) {
169+
Assert.assertEquals("#: 9 schema violations found", subjectException.getMessage());
170+
Assert.assertEquals(4, subjectException.getCausingExceptions().size());
171+
Assert.assertEquals(1, TestSupport.countCauseByJsonPointer(subjectException, "#"));
172+
Assert.assertEquals(1, TestSupport.countCauseByJsonPointer(subjectException, "#/numberProp"));
173+
Assert.assertEquals(1, TestSupport.countCauseByJsonPointer(subjectException, "#/stringPatternMatch"));
174+
Assert.assertEquals(1, TestSupport.countCauseByJsonPointer(subjectException, "#/nested"));
175+
176+
ValidationException nested1Exception = subjectException.getCausingExceptions().stream()
177+
.filter(ex -> ex.getPointerToViolation().equals("#/nested"))
178+
.findFirst()
179+
.get();
180+
Assert.assertEquals("#/nested: 6 schema violations found", nested1Exception.getMessage());
181+
Assert.assertEquals(4, nested1Exception.getCausingExceptions().size());
182+
Assert.assertEquals(1, TestSupport.countCauseByJsonPointer(nested1Exception, "#/nested"));
183+
Assert.assertEquals(1, TestSupport.countCauseByJsonPointer(nested1Exception, "#/nested/numberProp"));
184+
Assert.assertEquals(1, TestSupport.countCauseByJsonPointer(nested1Exception, "#/nested/stringPatternMatch"));
185+
Assert.assertEquals(1, TestSupport.countCauseByJsonPointer(nested1Exception, "#/nested/nested"));
186+
187+
ValidationException nested2Exception = nested1Exception.getCausingExceptions().stream()
188+
.filter(ex -> ex.getPointerToViolation().equals("#/nested/nested"))
189+
.findFirst()
190+
.get();
191+
Assert.assertEquals("#/nested/nested: 3 schema violations found", nested2Exception.getMessage());
192+
Assert.assertEquals(3, nested2Exception.getCausingExceptions().size());
193+
Assert.assertEquals(1, TestSupport.countCauseByJsonPointer(nested2Exception, "#/nested/nested"));
194+
Assert.assertEquals(1, TestSupport.countCauseByJsonPointer(nested2Exception, "#/nested/nested/numberProp"));
195+
Assert.assertEquals(1, TestSupport.countCauseByJsonPointer(nested2Exception, "#/nested/nested/stringPatternMatch"));
196+
197+
List<String> messages = subjectException.getAllMessages();
198+
Assert.assertEquals(9, messages.size());
199+
Assert.assertEquals(1, TestSupport.countMatchingMessage(messages, "#:"));
200+
Assert.assertEquals(1, TestSupport.countMatchingMessage(messages, "#/numberProp:"));
201+
Assert.assertEquals(1, TestSupport.countMatchingMessage(messages, "#/stringPatternMatch:"));
202+
Assert.assertEquals(1, TestSupport.countMatchingMessage(messages, "#/nested:"));
203+
Assert.assertEquals(1, TestSupport.countMatchingMessage(messages, "#/nested/numberProp:"));
204+
Assert.assertEquals(1, TestSupport.countMatchingMessage(messages, "#/nested/stringPatternMatch:"));
205+
Assert.assertEquals(1, TestSupport.countMatchingMessage(messages, "#/nested/nested:"));
206+
Assert.assertEquals(1, TestSupport.countMatchingMessage(messages, "#/nested/nested/numberProp:"));
207+
Assert.assertEquals(1, TestSupport.countMatchingMessage(messages, "#/nested/nested/stringPatternMatch:"));
142208
}
143209
}
144210

core/src/test/java/org/everit/json/schema/TestSupport.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
import org.junit.Assert;
1919

20+
import java.util.List;
21+
2022
public class TestSupport {
2123

2224
public static class Failure {
@@ -95,6 +97,12 @@ public static long countCauseByJsonPointer(final ValidationException root, final
9597
.count();
9698
}
9799

100+
public static long countMatchingMessage(final List<String> messages, final String expectedSubstring) {
101+
return messages.stream()
102+
.filter(message -> message.contains(expectedSubstring))
103+
.count();
104+
}
105+
98106
public static void expectFailure(final Schema failingSchema,
99107
final Class<? extends Schema> expectedViolatedSchemaClass,
100108
final String expectedPointer, final Object input) {

core/src/test/resources/org/everit/jsonvalidator/objecttestcases.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,18 @@
4040
"numberProp": "not number",
4141
"stringPatternMatch": 2
4242
},
43+
"multipleViolationsNested": {
44+
"numberProp": "not number",
45+
"stringPatternMatch": 2,
46+
"nested": {
47+
"numberProp": "not number 1",
48+
"stringPatternMatch": 11,
49+
"nested": {
50+
"numberProp": "not number 2",
51+
"stringPatternMatch": 22
52+
}
53+
}
54+
},
4355
"rectangleSingleFailure": {
4456
"rectangle": {
4557
"a": -5,

0 commit comments

Comments
 (0)