21
21
import org .slf4j .LoggerFactory ;
22
22
23
23
import java .util .*;
24
- import java .util .stream .Collectors ;
25
24
26
25
public class OneOfValidator extends BaseJsonValidator {
27
26
private static final Logger logger = LoggerFactory .getLogger (OneOfValidator .class );
28
27
29
- private final List <ShortcutValidator > schemas = new ArrayList <ShortcutValidator >();
30
-
31
- private static class ShortcutValidator {
32
- private final JsonSchema schema ;
33
- private final Map <String , String > constants ;
34
-
35
- ShortcutValidator (JsonNode schemaNode , JsonSchema parentSchema ,
36
- ValidationContext validationContext , JsonSchema schema ) {
37
- JsonNode refNode = schemaNode .get (ValidatorTypeCode .REF .getValue ());
38
- JsonSchema resolvedRefSchema = refNode != null && refNode .isTextual () ? RefValidator .getRefSchema (parentSchema , validationContext , refNode .textValue ()).getSchema () : null ;
39
- this .constants = extractConstants (schemaNode , resolvedRefSchema );
40
- this .schema = schema ;
41
- }
42
-
43
- private Map <String , String > extractConstants (JsonNode schemaNode , JsonSchema resolvedRefSchema ) {
44
- Map <String , String > refMap = resolvedRefSchema != null ? extractConstants (resolvedRefSchema .getSchemaNode ()) : Collections .<String , String >emptyMap ();
45
- Map <String , String > schemaMap = extractConstants (schemaNode );
46
- if (refMap .isEmpty ()) {
47
- return schemaMap ;
48
- }
49
- if (schemaMap .isEmpty ()) {
50
- return refMap ;
51
- }
52
- Map <String , String > joined = new HashMap <String , String >();
53
- joined .putAll (schemaMap );
54
- joined .putAll (refMap );
55
- return joined ;
56
- }
57
-
58
- private Map <String , String > extractConstants (JsonNode schemaNode ) {
59
- Map <String , String > result = new HashMap <String , String >();
60
- if (!schemaNode .isObject ()) {
61
- return result ;
62
- }
63
-
64
- JsonNode propertiesNode = schemaNode .get ("properties" );
65
- if (propertiesNode == null || !propertiesNode .isObject ()) {
66
- return result ;
67
- }
68
- Iterator <String > fit = propertiesNode .fieldNames ();
69
- while (fit .hasNext ()) {
70
- String fieldName = fit .next ();
71
- JsonNode jsonNode = propertiesNode .get (fieldName );
72
- String constantFieldValue = getConstantFieldValue (jsonNode );
73
- if (constantFieldValue != null && !constantFieldValue .isEmpty ()) {
74
- result .put (fieldName , constantFieldValue );
75
- }
76
- }
77
- return result ;
78
- }
79
-
80
- private String getConstantFieldValue (JsonNode jsonNode ) {
81
- if (jsonNode == null || !jsonNode .isObject () || !jsonNode .has ("enum" )) {
82
- return null ;
83
- }
84
- JsonNode enumNode = jsonNode .get ("enum" );
85
- if (enumNode == null || !enumNode .isArray ()) {
86
- return null ;
87
- }
88
- if (enumNode .size () != 1 ) {
89
- return null ;
90
- }
91
- JsonNode valueNode = enumNode .get (0 );
92
- if (valueNode == null || !valueNode .isTextual ()) {
93
- return null ;
94
- }
95
- return valueNode .textValue ();
96
- }
97
-
98
- public boolean allConstantsMatch (JsonNode node ) {
99
- for (Map .Entry <String , String > e : constants .entrySet ()) {
100
- JsonNode valueNode = node .get (e .getKey ());
101
- if (valueNode != null && valueNode .isTextual ()) {
102
- boolean match = e .getValue ().equals (valueNode .textValue ());
103
- if (!match ) {
104
- return false ;
105
- }
106
- }
107
- }
108
- return true ;
109
- }
110
-
111
- private JsonSchema getSchema () {
112
- return schema ;
113
- }
114
-
115
- }
28
+ private final List <JsonSchema > schemas = new ArrayList <>();
116
29
117
30
public OneOfValidator (String schemaPath , JsonNode schemaNode , JsonSchema parentSchema , ValidationContext validationContext ) {
118
31
super (schemaPath , schemaNode , parentSchema , ValidatorTypeCode .ONE_OF , validationContext );
119
32
int size = schemaNode .size ();
120
33
for (int i = 0 ; i < size ; i ++) {
121
34
JsonNode childNode = schemaNode .get (i );
122
- JsonSchema childSchema = new JsonSchema (validationContext , schemaPath + "/" + i , parentSchema .getCurrentUri (), childNode , parentSchema );
123
- schemas .add (new ShortcutValidator (childNode , parentSchema , validationContext , childSchema ));
35
+ schemas .add (new JsonSchema (validationContext , schemaPath + "/" + i , parentSchema .getCurrentUri (), childNode , parentSchema ));
124
36
}
125
37
parseErrorCode (getValidatorType ().getErrorCodeKey ());
126
38
}
127
39
128
40
public Set <ValidationMessage > validate (JsonNode node , JsonNode rootNode , String at ) {
129
- Set <ValidationMessage > errors = new LinkedHashSet <ValidationMessage >();
41
+ Set <ValidationMessage > errors = new LinkedHashSet <>();
130
42
131
43
// As oneOf might contain multiple schemas take a backup of evaluatedProperties.
132
44
Set <String > backupEvaluatedProperties = CollectorContext .getInstance ().copyEvaluatedProperties ();
@@ -143,28 +55,13 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
143
55
state .setComplexValidator (true );
144
56
145
57
int numberOfValidSchema = 0 ;
146
- Set <ValidationMessage > childErrors = new LinkedHashSet <ValidationMessage >();
147
- // validate that only a single element has been received in the oneOf node
148
- // validation should not continue, as it contradicts the oneOf requirement of only one
149
- // if(node.isObject() && node.size()>1) {
150
- // errors = Collections.singleton(buildValidationMessage(at, ""));
151
- // return Collections.unmodifiableSet(errors);
152
- // }
58
+ Set <ValidationMessage > childErrors = new LinkedHashSet <>();
153
59
154
- for (ShortcutValidator validator : schemas ) {
60
+ for (JsonSchema schema : schemas ) {
155
61
Set <ValidationMessage > schemaErrors = null ;
156
62
// Reset state in case the previous validator did not match
157
63
state .setMatchedNode (true );
158
64
159
- //This prevents from collecting all the error messages in proper format.
160
- /* if (!validator.allConstantsMatch(node)) {
161
- // take a shortcut: if there is any constant that does not match,
162
- // we can bail out of the validation
163
- continue;
164
- }*/
165
-
166
- // get the current validator
167
- JsonSchema schema = validator .schema ;
168
65
if (!state .isWalkEnabled ()) {
169
66
schemaErrors = schema .validate (node , rootNode , at );
170
67
} else {
@@ -188,37 +85,15 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
188
85
189
86
childErrors .addAll (schemaErrors );
190
87
}
191
- Set <ValidationMessage > childNotRequiredErrors = childErrors .stream ().filter (error -> !ValidatorTypeCode .REQUIRED .getValue ().equals (error .getType ())).collect (Collectors .toSet ());
192
88
193
89
// ensure there is always an "OneOf" error reported if number of valid schemas is not equal to 1.
194
- if (numberOfValidSchema > 1 ) {
195
- final ValidationMessage message = getMultiSchemasValidErrorMsg (at );
90
+ if (numberOfValidSchema != 1 ) {
91
+ ValidationMessage message = buildValidationMessage (at , Integer . toString ( numberOfValidSchema ) );
196
92
if (failFast ) {
197
93
throw new JsonSchemaException (message );
198
94
}
199
95
errors .add (message );
200
- }
201
-
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
- }
96
+ errors .addAll (childErrors );
222
97
}
223
98
224
99
// Make sure to signal parent handlers we matched
@@ -238,85 +113,29 @@ else if (numberOfValidSchema < 1) {
238
113
}
239
114
}
240
115
241
- /**
242
- * Sort <code>ValidationMessage</code> by its type
243
- * @return
244
- */
245
- private static int compareValidationMessages (ValidationMessage vm1 , ValidationMessage vm2 ) {
246
- // ValidationMessage's type has smaller index in the list below has high priority
247
- final List <String > typeCodes = Arrays .asList (
248
- ValidatorTypeCode .TYPE .getValue (),
249
- ValidatorTypeCode .DATETIME .getValue (),
250
- ValidatorTypeCode .UUID .getValue (),
251
- ValidatorTypeCode .ID .getValue (),
252
- ValidatorTypeCode .EXCLUSIVE_MAXIMUM .getValue (),
253
- ValidatorTypeCode .EXCLUSIVE_MINIMUM .getValue (),
254
- ValidatorTypeCode .TRUE .getValue (),
255
- ValidatorTypeCode .FALSE .getValue (),
256
- ValidatorTypeCode .CONST .getValue (),
257
- ValidatorTypeCode .CONTAINS .getValue (),
258
- ValidatorTypeCode .PROPERTYNAMES .getValue ()
259
- );
260
-
261
- final int index1 = typeCodes .indexOf (vm1 .getType ());
262
- final int index2 = typeCodes .indexOf (vm2 .getType ());
263
-
264
- if (index1 >= 0 ) {
265
- if (index2 >= 0 ) {
266
- return Integer .compare (index1 , index2 );
267
- } else {
268
- return -1 ;
269
- }
270
- } else {
271
- if (index2 >= 0 ) {
272
- return 1 ;
273
- } else {
274
- return vm1 .getCode ().compareTo (vm2 .getCode ());
275
- }
276
- }
277
- }
278
-
279
116
private void resetValidatorState () {
280
117
ValidatorState state = (ValidatorState ) CollectorContext .getInstance ().get (ValidatorState .VALIDATOR_STATE_KEY );
281
118
state .setComplexValidator (false );
282
119
state .setMatchedNode (true );
283
120
}
284
121
285
- public List <JsonSchema > getChildSchemas () {
286
- List <JsonSchema > childJsonSchemas = new ArrayList <JsonSchema >();
287
- for (ShortcutValidator shortcutValidator : schemas ) {
288
- childJsonSchemas .add (shortcutValidator .getSchema ());
289
- }
290
- return childJsonSchemas ;
291
- }
292
-
293
122
@ Override
294
123
public Set <ValidationMessage > walk (JsonNode node , JsonNode rootNode , String at , boolean shouldValidateSchema ) {
295
124
HashSet <ValidationMessage > validationMessages = new LinkedHashSet <ValidationMessage >();
296
125
if (shouldValidateSchema ) {
297
126
validationMessages .addAll (validate (node , rootNode , at ));
298
127
} else {
299
- for (ShortcutValidator validator : schemas ) {
300
- validator . schema .walk (node , rootNode , at , shouldValidateSchema );
128
+ for (JsonSchema schema : schemas ) {
129
+ schema .walk (node , rootNode , at , shouldValidateSchema );
301
130
}
302
131
}
303
132
return validationMessages ;
304
133
}
305
134
306
- private ValidationMessage getMultiSchemasValidErrorMsg (String at ){
307
- String msg ="" ;
308
- for (ShortcutValidator schema : schemas ){
309
- String schemaValue = schema .getSchema ().getSchemaNode ().toString ();
310
- msg = msg .concat (schemaValue );
311
- }
312
-
313
- return ValidationMessage .of (getValidatorType ().getValue (), ValidatorTypeCode .ONE_OF , at , schemaPath , msg );
314
- }
315
-
316
135
@ Override
317
136
public void preloadJsonSchema () {
318
- for (final ShortcutValidator scValidator : schemas ) {
319
- scValidator . getSchema () .initializeValidators ();
137
+ for (JsonSchema schema : schemas ) {
138
+ schema .initializeValidators ();
320
139
}
321
140
}
322
141
}
0 commit comments