Skip to content

Commit 0a9618a

Browse files
committed
Add ValidationException#getAllMessages()
Walk through all nested violations and return the list of error messages. This is particularly useful for use by a REST API when validating the schema of an incoming JSON payload. The flat list of messages provides a convenient format for clients to log, and even parse, the errors.
1 parent 7b3a5d6 commit 0a9618a

File tree

3 files changed

+52
-2
lines changed

3 files changed

+52
-2
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: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.junit.Assert;
2323
import org.junit.Test;
2424

25+
import java.util.List;
2526
import java.util.concurrent.Callable;
2627

2728
import static org.junit.Assert.assertTrue;
@@ -136,9 +137,15 @@ 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:"));
142149
}
143150
}
144151

@@ -186,6 +193,18 @@ public void multipleViolationsNested() throws Exception {
186193
Assert.assertEquals(1, TestSupport.countCauseByJsonPointer(nested2Exception, "#/nested/nested"));
187194
Assert.assertEquals(1, TestSupport.countCauseByJsonPointer(nested2Exception, "#/nested/nested/numberProp"));
188195
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:"));
189208
}
190209
}
191210

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) {

0 commit comments

Comments
 (0)