@@ -126,7 +126,7 @@ public OneOfValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentS
126126 }
127127
128128 public Set <ValidationMessage > validate (JsonNode node , JsonNode rootNode , String at ) {
129- Set <ValidationMessage > errors = new LinkedHashSet <>();
129+ Set <ValidationMessage > errors = new LinkedHashSet <ValidationMessage >();
130130
131131 // As oneOf might contain multiple schemas take a backup of evaluatedProperties.
132132 Object backupEvaluatedProperties = CollectorContext .getInstance ().get (UnEvaluatedPropertiesValidator .EVALUATED_PROPERTIES );
@@ -143,13 +143,14 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
143143 state .setComplexValidator (true );
144144
145145 int numberOfValidSchema = 0 ;
146- Set <ValidationMessage > childErrors = new LinkedHashSet <>();
146+ Set <ValidationMessage > childErrors = new LinkedHashSet <ValidationMessage >();
147147 // validate that only a single element has been received in the oneOf node
148148 // validation should not continue, as it contradicts the oneOf requirement of only one
149149// if(node.isObject() && node.size()>1) {
150150// errors = Collections.singleton(buildValidationMessage(at, ""));
151151// return Collections.unmodifiableSet(errors);
152152// }
153+
153154 for (ShortcutValidator validator : schemas ) {
154155 Set <ValidationMessage > schemaErrors = null ;
155156 // Reset state in case the previous validator did not match
@@ -190,15 +191,36 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
190191 Set <ValidationMessage > childNotRequiredErrors = childErrors .stream ().filter (error -> !ValidatorTypeCode .REQUIRED .getValue ().equals (error .getType ())).collect (Collectors .toSet ());
191192
192193 // ensure there is always an "OneOf" error reported if number of valid schemas is not equal to 1.
193- // TODO We need to break it into two errors in the future.
194- if (numberOfValidSchema != 1 ) {
194+ if (numberOfValidSchema > 1 ) {
195195 final ValidationMessage message = getMultiSchemasValidErrorMsg (at );
196196 if (failFast ) {
197197 throw new JsonSchemaException (message );
198198 }
199199 errors .add (message );
200200 }
201201
202+ // ensure there is always an "OneOf" error reported if number of valid schemas is not equal to 1.
203+ else if (numberOfValidSchema < 1 ) {
204+ if (!childNotRequiredErrors .isEmpty ()) {
205+ childErrors = childNotRequiredErrors ;
206+ }
207+ if (!childErrors .isEmpty ()) {
208+ if (childErrors .size () > 1 ) {
209+ Set <ValidationMessage > notAdditionalPropertiesOnly = new LinkedHashSet <>(childErrors .stream ()
210+ .filter ((ValidationMessage validationMessage ) -> !ValidatorTypeCode .ADDITIONAL_PROPERTIES .getValue ().equals (validationMessage .getType ()))
211+ .sorted ((vm1 , vm2 ) -> compareValidationMessages (vm1 , vm2 ))
212+ .collect (Collectors .toList ()));
213+ if (notAdditionalPropertiesOnly .size () > 0 ) {
214+ childErrors = notAdditionalPropertiesOnly ;
215+ }
216+ }
217+ errors .addAll (childErrors );
218+ }
219+ if (failFast ) {
220+ throw new JsonSchemaException (errors .toString ());
221+ }
222+ }
223+
202224 // Make sure to signal parent handlers we matched
203225 if (errors .isEmpty ())
204226 state .setMatchedNode (true );
@@ -218,34 +240,84 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
218240 }
219241 }
220242
243+ /**
244+ * Sort <code>ValidationMessage</code> by its type
245+ * @return
246+ */
247+ private static int compareValidationMessages (ValidationMessage vm1 , ValidationMessage vm2 ) {
248+ // ValidationMessage's type has smaller index in the list below has high priority
249+ final List <String > typeCodes = Arrays .asList (
250+ ValidatorTypeCode .TYPE .getValue (),
251+ ValidatorTypeCode .DATETIME .getValue (),
252+ ValidatorTypeCode .UUID .getValue (),
253+ ValidatorTypeCode .ID .getValue (),
254+ ValidatorTypeCode .EXCLUSIVE_MAXIMUM .getValue (),
255+ ValidatorTypeCode .EXCLUSIVE_MINIMUM .getValue (),
256+ ValidatorTypeCode .TRUE .getValue (),
257+ ValidatorTypeCode .FALSE .getValue (),
258+ ValidatorTypeCode .CONST .getValue (),
259+ ValidatorTypeCode .CONTAINS .getValue (),
260+ ValidatorTypeCode .PROPERTYNAMES .getValue ()
261+ );
262+
263+ final int index1 = typeCodes .indexOf (vm1 .getType ());
264+ final int index2 = typeCodes .indexOf (vm2 .getType ());
265+
266+ if (index1 >= 0 ) {
267+ if (index2 >= 0 ) {
268+ return Integer .compare (index1 , index2 );
269+ } else {
270+ return -1 ;
271+ }
272+ } else {
273+ if (index2 >= 0 ) {
274+ return 1 ;
275+ } else {
276+ return vm1 .getCode ().compareTo (vm2 .getCode ());
277+ }
278+ }
279+ }
280+
221281 private void resetValidatorState () {
222282 ValidatorState state = (ValidatorState ) CollectorContext .getInstance ().get (ValidatorState .VALIDATOR_STATE_KEY );
223283 state .setComplexValidator (false );
224284 state .setMatchedNode (true );
225285 }
226286
287+ public List <JsonSchema > getChildSchemas () {
288+ List <JsonSchema > childJsonSchemas = new ArrayList <JsonSchema >();
289+ for (ShortcutValidator shortcutValidator : schemas ) {
290+ childJsonSchemas .add (shortcutValidator .getSchema ());
291+ }
292+ return childJsonSchemas ;
293+ }
294+
227295 @ Override
228296 public Set <ValidationMessage > walk (JsonNode node , JsonNode rootNode , String at , boolean shouldValidateSchema ) {
229297 HashSet <ValidationMessage > validationMessages = new LinkedHashSet <ValidationMessage >();
230298 if (shouldValidateSchema ) {
231299 validationMessages .addAll (validate (node , rootNode , at ));
232300 } else {
233301 for (ShortcutValidator validator : schemas ) {
234- validator .schema .walk (node , rootNode , at , shouldValidateSchema );
302+ validator .schema .walk (node , rootNode , at , shouldValidateSchema );
235303 }
236304 }
237305 return validationMessages ;
238306 }
239307
240- private ValidationMessage getMultiSchemasValidErrorMsg (String at ) {
241- List <String > msgStrList = schemas .stream ().map (shortcutValidator -> shortcutValidator .getSchema ().getSchemaNode ().toString ()).collect (Collectors .toList ());
242- String msg = String .join (", " , msgStrList );
308+ private ValidationMessage getMultiSchemasValidErrorMsg (String at ){
309+ String msg ="" ;
310+ for (ShortcutValidator schema : schemas ){
311+ String schemaValue = schema .getSchema ().getSchemaNode ().toString ();
312+ msg = msg .concat (schemaValue );
313+ }
314+
243315 return ValidationMessage .of (getValidatorType ().getValue (), ValidatorTypeCode .ONE_OF , at , msg );
244316 }
245317
246318 @ Override
247319 public void preloadJsonSchema () {
248- for (final ShortcutValidator scValidator : schemas ) {
320+ for (final ShortcutValidator scValidator : schemas ) {
249321 scValidator .getSchema ().initializeValidators ();
250322 }
251323 }
0 commit comments