1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 package gojsonschema
27
28 import (
29 "encoding/json"
30 "math/big"
31 "reflect"
32 "regexp"
33 "strconv"
34 "strings"
35 "unicode/utf8"
36 )
37
38
39 func Validate(ls JSONLoader, ld JSONLoader) (*Result, error) {
40
41 schema, err := NewSchema(ls)
42 if err != nil {
43 return nil, err
44 }
45 return schema.Validate(ld)
46 }
47
48
49 func (v *Schema) Validate(l JSONLoader) (*Result, error) {
50 root, err := l.LoadJSON()
51 if err != nil {
52 return nil, err
53 }
54 return v.validateDocument(root), nil
55 }
56
57 func (v *Schema) validateDocument(root interface{}) *Result {
58 result := &Result{}
59 context := NewJsonContext(STRING_CONTEXT_ROOT, nil)
60 v.rootSchema.validateRecursive(v.rootSchema, root, result, context)
61 return result
62 }
63
64 func (v *subSchema) subValidateWithContext(document interface{}, context *JsonContext) *Result {
65 result := &Result{}
66 v.validateRecursive(v, document, result, context)
67 return result
68 }
69
70
71 func (v *subSchema) validateRecursive(currentSubSchema *subSchema, currentNode interface{}, result *Result, context *JsonContext) {
72
73 if internalLogEnabled {
74 internalLog("validateRecursive %s", context.String())
75 internalLog(" %v", currentNode)
76 }
77
78
79 if currentSubSchema.pass != nil {
80 if !*currentSubSchema.pass {
81 result.addInternalError(
82 new(FalseError),
83 context,
84 currentNode,
85 ErrorDetails{},
86 )
87 }
88 return
89 }
90
91
92 if currentSubSchema.refSchema != nil {
93 v.validateRecursive(currentSubSchema.refSchema, currentNode, result, context)
94 return
95 }
96
97
98 if currentNode == nil {
99 if currentSubSchema.types.IsTyped() && !currentSubSchema.types.Contains(TYPE_NULL) {
100 result.addInternalError(
101 new(InvalidTypeError),
102 context,
103 currentNode,
104 ErrorDetails{
105 "expected": currentSubSchema.types.String(),
106 "given": TYPE_NULL,
107 },
108 )
109 return
110 }
111
112 currentSubSchema.validateSchema(currentSubSchema, currentNode, result, context)
113 v.validateCommon(currentSubSchema, currentNode, result, context)
114
115 } else {
116
117 if isJSONNumber(currentNode) {
118
119 value := currentNode.(json.Number)
120
121 isInt := checkJSONInteger(value)
122
123 validType := currentSubSchema.types.Contains(TYPE_NUMBER) || (isInt && currentSubSchema.types.Contains(TYPE_INTEGER))
124
125 if currentSubSchema.types.IsTyped() && !validType {
126
127 givenType := TYPE_INTEGER
128 if !isInt {
129 givenType = TYPE_NUMBER
130 }
131
132 result.addInternalError(
133 new(InvalidTypeError),
134 context,
135 currentNode,
136 ErrorDetails{
137 "expected": currentSubSchema.types.String(),
138 "given": givenType,
139 },
140 )
141 return
142 }
143
144 currentSubSchema.validateSchema(currentSubSchema, value, result, context)
145 v.validateNumber(currentSubSchema, value, result, context)
146 v.validateCommon(currentSubSchema, value, result, context)
147 v.validateString(currentSubSchema, value, result, context)
148
149 } else {
150
151 rValue := reflect.ValueOf(currentNode)
152 rKind := rValue.Kind()
153
154 switch rKind {
155
156
157
158 case reflect.Slice:
159
160 if currentSubSchema.types.IsTyped() && !currentSubSchema.types.Contains(TYPE_ARRAY) {
161 result.addInternalError(
162 new(InvalidTypeError),
163 context,
164 currentNode,
165 ErrorDetails{
166 "expected": currentSubSchema.types.String(),
167 "given": TYPE_ARRAY,
168 },
169 )
170 return
171 }
172
173 castCurrentNode := currentNode.([]interface{})
174
175 currentSubSchema.validateSchema(currentSubSchema, castCurrentNode, result, context)
176
177 v.validateArray(currentSubSchema, castCurrentNode, result, context)
178 v.validateCommon(currentSubSchema, castCurrentNode, result, context)
179
180
181
182 case reflect.Map:
183 if currentSubSchema.types.IsTyped() && !currentSubSchema.types.Contains(TYPE_OBJECT) {
184 result.addInternalError(
185 new(InvalidTypeError),
186 context,
187 currentNode,
188 ErrorDetails{
189 "expected": currentSubSchema.types.String(),
190 "given": TYPE_OBJECT,
191 },
192 )
193 return
194 }
195
196 castCurrentNode, ok := currentNode.(map[string]interface{})
197 if !ok {
198 castCurrentNode = convertDocumentNode(currentNode).(map[string]interface{})
199 }
200
201 currentSubSchema.validateSchema(currentSubSchema, castCurrentNode, result, context)
202
203 v.validateObject(currentSubSchema, castCurrentNode, result, context)
204 v.validateCommon(currentSubSchema, castCurrentNode, result, context)
205
206 for _, pSchema := range currentSubSchema.propertiesChildren {
207 nextNode, ok := castCurrentNode[pSchema.property]
208 if ok {
209 subContext := NewJsonContext(pSchema.property, context)
210 v.validateRecursive(pSchema, nextNode, result, subContext)
211 }
212 }
213
214
215
216 case reflect.Bool:
217
218 if currentSubSchema.types.IsTyped() && !currentSubSchema.types.Contains(TYPE_BOOLEAN) {
219 result.addInternalError(
220 new(InvalidTypeError),
221 context,
222 currentNode,
223 ErrorDetails{
224 "expected": currentSubSchema.types.String(),
225 "given": TYPE_BOOLEAN,
226 },
227 )
228 return
229 }
230
231 value := currentNode.(bool)
232
233 currentSubSchema.validateSchema(currentSubSchema, value, result, context)
234 v.validateNumber(currentSubSchema, value, result, context)
235 v.validateCommon(currentSubSchema, value, result, context)
236 v.validateString(currentSubSchema, value, result, context)
237
238 case reflect.String:
239
240 if currentSubSchema.types.IsTyped() && !currentSubSchema.types.Contains(TYPE_STRING) {
241 result.addInternalError(
242 new(InvalidTypeError),
243 context,
244 currentNode,
245 ErrorDetails{
246 "expected": currentSubSchema.types.String(),
247 "given": TYPE_STRING,
248 },
249 )
250 return
251 }
252
253 value := currentNode.(string)
254
255 currentSubSchema.validateSchema(currentSubSchema, value, result, context)
256 v.validateNumber(currentSubSchema, value, result, context)
257 v.validateCommon(currentSubSchema, value, result, context)
258 v.validateString(currentSubSchema, value, result, context)
259
260 }
261
262 }
263
264 }
265
266 result.incrementScore()
267 }
268
269
270 func (v *subSchema) validateSchema(currentSubSchema *subSchema, currentNode interface{}, result *Result, context *JsonContext) {
271
272 if internalLogEnabled {
273 internalLog("validateSchema %s", context.String())
274 internalLog(" %v", currentNode)
275 }
276
277 if len(currentSubSchema.anyOf) > 0 {
278
279 validatedAnyOf := false
280 var bestValidationResult *Result
281
282 for _, anyOfSchema := range currentSubSchema.anyOf {
283 if !validatedAnyOf {
284 validationResult := anyOfSchema.subValidateWithContext(currentNode, context)
285 validatedAnyOf = validationResult.Valid()
286
287 if !validatedAnyOf && (bestValidationResult == nil || validationResult.score > bestValidationResult.score) {
288 bestValidationResult = validationResult
289 }
290 }
291 }
292 if !validatedAnyOf {
293
294 result.addInternalError(new(NumberAnyOfError), context, currentNode, ErrorDetails{})
295
296 if bestValidationResult != nil {
297
298
299 result.mergeErrors(bestValidationResult)
300 }
301 }
302 }
303
304 if len(currentSubSchema.oneOf) > 0 {
305
306 nbValidated := 0
307 var bestValidationResult *Result
308
309 for _, oneOfSchema := range currentSubSchema.oneOf {
310 validationResult := oneOfSchema.subValidateWithContext(currentNode, context)
311 if validationResult.Valid() {
312 nbValidated++
313 } else if nbValidated == 0 && (bestValidationResult == nil || validationResult.score > bestValidationResult.score) {
314 bestValidationResult = validationResult
315 }
316 }
317
318 if nbValidated != 1 {
319
320 result.addInternalError(new(NumberOneOfError), context, currentNode, ErrorDetails{})
321
322 if nbValidated == 0 {
323
324
325 result.mergeErrors(bestValidationResult)
326 }
327 }
328
329 }
330
331 if len(currentSubSchema.allOf) > 0 {
332 nbValidated := 0
333
334 for _, allOfSchema := range currentSubSchema.allOf {
335 validationResult := allOfSchema.subValidateWithContext(currentNode, context)
336 if validationResult.Valid() {
337 nbValidated++
338 }
339 result.mergeErrors(validationResult)
340 }
341
342 if nbValidated != len(currentSubSchema.allOf) {
343 result.addInternalError(new(NumberAllOfError), context, currentNode, ErrorDetails{})
344 }
345 }
346
347 if currentSubSchema.not != nil {
348 validationResult := currentSubSchema.not.subValidateWithContext(currentNode, context)
349 if validationResult.Valid() {
350 result.addInternalError(new(NumberNotError), context, currentNode, ErrorDetails{})
351 }
352 }
353
354 if currentSubSchema.dependencies != nil && len(currentSubSchema.dependencies) > 0 {
355 if isKind(currentNode, reflect.Map) {
356 for elementKey := range currentNode.(map[string]interface{}) {
357 if dependency, ok := currentSubSchema.dependencies[elementKey]; ok {
358 switch dependency := dependency.(type) {
359
360 case []string:
361 for _, dependOnKey := range dependency {
362 if _, dependencyResolved := currentNode.(map[string]interface{})[dependOnKey]; !dependencyResolved {
363 result.addInternalError(
364 new(MissingDependencyError),
365 context,
366 currentNode,
367 ErrorDetails{"dependency": dependOnKey},
368 )
369 }
370 }
371
372 case *subSchema:
373 dependency.validateRecursive(dependency, currentNode, result, context)
374 }
375 }
376 }
377 }
378 }
379
380 if currentSubSchema._if != nil {
381 validationResultIf := currentSubSchema._if.subValidateWithContext(currentNode, context)
382 if currentSubSchema._then != nil && validationResultIf.Valid() {
383 validationResultThen := currentSubSchema._then.subValidateWithContext(currentNode, context)
384 if !validationResultThen.Valid() {
385 result.addInternalError(new(ConditionThenError), context, currentNode, ErrorDetails{})
386 result.mergeErrors(validationResultThen)
387 }
388 }
389 if currentSubSchema._else != nil && !validationResultIf.Valid() {
390 validationResultElse := currentSubSchema._else.subValidateWithContext(currentNode, context)
391 if !validationResultElse.Valid() {
392 result.addInternalError(new(ConditionElseError), context, currentNode, ErrorDetails{})
393 result.mergeErrors(validationResultElse)
394 }
395 }
396 }
397
398 result.incrementScore()
399 }
400
401 func (v *subSchema) validateCommon(currentSubSchema *subSchema, value interface{}, result *Result, context *JsonContext) {
402
403 if internalLogEnabled {
404 internalLog("validateCommon %s", context.String())
405 internalLog(" %v", value)
406 }
407
408
409 if currentSubSchema._const != nil {
410 vString, err := marshalWithoutNumber(value)
411 if err != nil {
412 result.addInternalError(new(InternalError), context, value, ErrorDetails{"error": err})
413 }
414 if *vString != *currentSubSchema._const {
415 result.addInternalError(new(ConstError),
416 context,
417 value,
418 ErrorDetails{
419 "allowed": *currentSubSchema._const,
420 },
421 )
422 }
423 }
424
425
426 if len(currentSubSchema.enum) > 0 {
427 vString, err := marshalWithoutNumber(value)
428 if err != nil {
429 result.addInternalError(new(InternalError), context, value, ErrorDetails{"error": err})
430 }
431 if !isStringInSlice(currentSubSchema.enum, *vString) {
432 result.addInternalError(
433 new(EnumError),
434 context,
435 value,
436 ErrorDetails{
437 "allowed": strings.Join(currentSubSchema.enum, ", "),
438 },
439 )
440 }
441 }
442
443 result.incrementScore()
444 }
445
446 func (v *subSchema) validateArray(currentSubSchema *subSchema, value []interface{}, result *Result, context *JsonContext) {
447
448 if internalLogEnabled {
449 internalLog("validateArray %s", context.String())
450 internalLog(" %v", value)
451 }
452
453 nbValues := len(value)
454
455
456 if currentSubSchema.itemsChildrenIsSingleSchema {
457 for i := range value {
458 subContext := NewJsonContext(strconv.Itoa(i), context)
459 validationResult := currentSubSchema.itemsChildren[0].subValidateWithContext(value[i], subContext)
460 result.mergeErrors(validationResult)
461 }
462 } else {
463 if currentSubSchema.itemsChildren != nil && len(currentSubSchema.itemsChildren) > 0 {
464
465 nbItems := len(currentSubSchema.itemsChildren)
466
467
468 for i := 0; i != nbItems && i != nbValues; i++ {
469 subContext := NewJsonContext(strconv.Itoa(i), context)
470 validationResult := currentSubSchema.itemsChildren[i].subValidateWithContext(value[i], subContext)
471 result.mergeErrors(validationResult)
472 }
473
474 if nbItems < nbValues {
475
476
477
478 switch currentSubSchema.additionalItems.(type) {
479 case bool:
480 if !currentSubSchema.additionalItems.(bool) {
481 result.addInternalError(new(ArrayNoAdditionalItemsError), context, value, ErrorDetails{})
482 }
483 case *subSchema:
484 additionalItemSchema := currentSubSchema.additionalItems.(*subSchema)
485 for i := nbItems; i != nbValues; i++ {
486 subContext := NewJsonContext(strconv.Itoa(i), context)
487 validationResult := additionalItemSchema.subValidateWithContext(value[i], subContext)
488 result.mergeErrors(validationResult)
489 }
490 }
491 }
492 }
493 }
494
495
496 if currentSubSchema.minItems != nil {
497 if nbValues < int(*currentSubSchema.minItems) {
498 result.addInternalError(
499 new(ArrayMinItemsError),
500 context,
501 value,
502 ErrorDetails{"min": *currentSubSchema.minItems},
503 )
504 }
505 }
506 if currentSubSchema.maxItems != nil {
507 if nbValues > int(*currentSubSchema.maxItems) {
508 result.addInternalError(
509 new(ArrayMaxItemsError),
510 context,
511 value,
512 ErrorDetails{"max": *currentSubSchema.maxItems},
513 )
514 }
515 }
516
517
518 if currentSubSchema.uniqueItems {
519 var stringifiedItems = make(map[string]int)
520 for j, v := range value {
521 vString, err := marshalWithoutNumber(v)
522 if err != nil {
523 result.addInternalError(new(InternalError), context, value, ErrorDetails{"err": err})
524 }
525 if i, ok := stringifiedItems[*vString]; ok {
526 result.addInternalError(
527 new(ItemsMustBeUniqueError),
528 context,
529 value,
530 ErrorDetails{"type": TYPE_ARRAY, "i": i, "j": j},
531 )
532 }
533 stringifiedItems[*vString] = j
534 }
535 }
536
537
538
539 if currentSubSchema.contains != nil {
540 validatedOne := false
541 var bestValidationResult *Result
542
543 for i, v := range value {
544 subContext := NewJsonContext(strconv.Itoa(i), context)
545
546 validationResult := currentSubSchema.contains.subValidateWithContext(v, subContext)
547 if validationResult.Valid() {
548 validatedOne = true
549 break
550 } else {
551 if bestValidationResult == nil || validationResult.score > bestValidationResult.score {
552 bestValidationResult = validationResult
553 }
554 }
555 }
556 if !validatedOne {
557 result.addInternalError(
558 new(ArrayContainsError),
559 context,
560 value,
561 ErrorDetails{},
562 )
563 if bestValidationResult != nil {
564 result.mergeErrors(bestValidationResult)
565 }
566 }
567 }
568
569 result.incrementScore()
570 }
571
572 func (v *subSchema) validateObject(currentSubSchema *subSchema, value map[string]interface{}, result *Result, context *JsonContext) {
573
574 if internalLogEnabled {
575 internalLog("validateObject %s", context.String())
576 internalLog(" %v", value)
577 }
578
579
580 if currentSubSchema.minProperties != nil {
581 if len(value) < int(*currentSubSchema.minProperties) {
582 result.addInternalError(
583 new(ArrayMinPropertiesError),
584 context,
585 value,
586 ErrorDetails{"min": *currentSubSchema.minProperties},
587 )
588 }
589 }
590 if currentSubSchema.maxProperties != nil {
591 if len(value) > int(*currentSubSchema.maxProperties) {
592 result.addInternalError(
593 new(ArrayMaxPropertiesError),
594 context,
595 value,
596 ErrorDetails{"max": *currentSubSchema.maxProperties},
597 )
598 }
599 }
600
601
602 for _, requiredProperty := range currentSubSchema.required {
603 _, ok := value[requiredProperty]
604 if ok {
605 result.incrementScore()
606 } else {
607 result.addInternalError(
608 new(RequiredError),
609 context,
610 value,
611 ErrorDetails{"property": requiredProperty},
612 )
613 }
614 }
615
616
617 for pk := range value {
618
619
620 found := false
621 for _, spValue := range currentSubSchema.propertiesChildren {
622 if pk == spValue.property {
623 found = true
624 }
625 }
626
627
628 ppMatch := v.validatePatternProperty(currentSubSchema, pk, value[pk], result, context)
629
630
631 if !found && !ppMatch {
632 switch ap := currentSubSchema.additionalProperties.(type) {
633 case bool:
634
635 if !ap {
636 result.addInternalError(
637 new(AdditionalPropertyNotAllowedError),
638 context,
639 value[pk],
640 ErrorDetails{"property": pk},
641 )
642
643 }
644 case *subSchema:
645 validationResult := ap.subValidateWithContext(value[pk], NewJsonContext(pk, context))
646 result.mergeErrors(validationResult)
647 }
648 }
649 }
650
651
652 if currentSubSchema.propertyNames != nil {
653 for pk := range value {
654 validationResult := currentSubSchema.propertyNames.subValidateWithContext(pk, context)
655 if !validationResult.Valid() {
656 result.addInternalError(new(InvalidPropertyNameError),
657 context,
658 value, ErrorDetails{
659 "property": pk,
660 })
661 result.mergeErrors(validationResult)
662 }
663 }
664 }
665
666 result.incrementScore()
667 }
668
669 func (v *subSchema) validatePatternProperty(currentSubSchema *subSchema, key string, value interface{}, result *Result, context *JsonContext) bool {
670
671 if internalLogEnabled {
672 internalLog("validatePatternProperty %s", context.String())
673 internalLog(" %s %v", key, value)
674 }
675
676 validated := false
677
678 for pk, pv := range currentSubSchema.patternProperties {
679 if matches, _ := regexp.MatchString(pk, key); matches {
680 validated = true
681 subContext := NewJsonContext(key, context)
682 validationResult := pv.subValidateWithContext(value, subContext)
683 result.mergeErrors(validationResult)
684 }
685 }
686
687 if !validated {
688 return false
689 }
690
691 result.incrementScore()
692 return true
693 }
694
695 func (v *subSchema) validateString(currentSubSchema *subSchema, value interface{}, result *Result, context *JsonContext) {
696
697
698 if isJSONNumber(value) {
699 return
700 }
701
702
703 if !isKind(value, reflect.String) {
704 return
705 }
706
707 if internalLogEnabled {
708 internalLog("validateString %s", context.String())
709 internalLog(" %v", value)
710 }
711
712 stringValue := value.(string)
713
714
715 if currentSubSchema.minLength != nil {
716 if utf8.RuneCount([]byte(stringValue)) < int(*currentSubSchema.minLength) {
717 result.addInternalError(
718 new(StringLengthGTEError),
719 context,
720 value,
721 ErrorDetails{"min": *currentSubSchema.minLength},
722 )
723 }
724 }
725 if currentSubSchema.maxLength != nil {
726 if utf8.RuneCount([]byte(stringValue)) > int(*currentSubSchema.maxLength) {
727 result.addInternalError(
728 new(StringLengthLTEError),
729 context,
730 value,
731 ErrorDetails{"max": *currentSubSchema.maxLength},
732 )
733 }
734 }
735
736
737 if currentSubSchema.pattern != nil {
738 if !currentSubSchema.pattern.MatchString(stringValue) {
739 result.addInternalError(
740 new(DoesNotMatchPatternError),
741 context,
742 value,
743 ErrorDetails{"pattern": currentSubSchema.pattern},
744 )
745
746 }
747 }
748
749
750 if currentSubSchema.format != "" {
751 if !FormatCheckers.IsFormat(currentSubSchema.format, stringValue) {
752 result.addInternalError(
753 new(DoesNotMatchFormatError),
754 context,
755 value,
756 ErrorDetails{"format": currentSubSchema.format},
757 )
758 }
759 }
760
761 result.incrementScore()
762 }
763
764 func (v *subSchema) validateNumber(currentSubSchema *subSchema, value interface{}, result *Result, context *JsonContext) {
765
766
767 if !isJSONNumber(value) {
768 return
769 }
770
771 if internalLogEnabled {
772 internalLog("validateNumber %s", context.String())
773 internalLog(" %v", value)
774 }
775
776 number := value.(json.Number)
777 float64Value, _ := new(big.Rat).SetString(string(number))
778
779
780 if currentSubSchema.multipleOf != nil {
781 if q := new(big.Rat).Quo(float64Value, currentSubSchema.multipleOf); !q.IsInt() {
782 result.addInternalError(
783 new(MultipleOfError),
784 context,
785 number,
786 ErrorDetails{
787 "multiple": new(big.Float).SetRat(currentSubSchema.multipleOf),
788 },
789 )
790 }
791 }
792
793
794 if currentSubSchema.maximum != nil {
795 if float64Value.Cmp(currentSubSchema.maximum) == 1 {
796 result.addInternalError(
797 new(NumberLTEError),
798 context,
799 number,
800 ErrorDetails{
801 "max": new(big.Float).SetRat(currentSubSchema.maximum),
802 },
803 )
804 }
805 }
806 if currentSubSchema.exclusiveMaximum != nil {
807 if float64Value.Cmp(currentSubSchema.exclusiveMaximum) >= 0 {
808 result.addInternalError(
809 new(NumberLTError),
810 context,
811 number,
812 ErrorDetails{
813 "max": new(big.Float).SetRat(currentSubSchema.exclusiveMaximum),
814 },
815 )
816 }
817 }
818
819
820 if currentSubSchema.minimum != nil {
821 if float64Value.Cmp(currentSubSchema.minimum) == -1 {
822 result.addInternalError(
823 new(NumberGTEError),
824 context,
825 number,
826 ErrorDetails{
827 "min": new(big.Float).SetRat(currentSubSchema.minimum),
828 },
829 )
830 }
831 }
832 if currentSubSchema.exclusiveMinimum != nil {
833 if float64Value.Cmp(currentSubSchema.exclusiveMinimum) <= 0 {
834 result.addInternalError(
835 new(NumberGTError),
836 context,
837 number,
838 ErrorDetails{
839 "min": new(big.Float).SetRat(currentSubSchema.exclusiveMinimum),
840 },
841 )
842 }
843 }
844
845
846 if currentSubSchema.format != "" {
847 if !FormatCheckers.IsFormat(currentSubSchema.format, float64Value) {
848 result.addInternalError(
849 new(DoesNotMatchFormatError),
850 context,
851 value,
852 ErrorDetails{"format": currentSubSchema.format},
853 )
854 }
855 }
856
857 result.incrementScore()
858 }
859
View as plain text