github.com/kaptinlin/jsonschema@v0.4.6/contains.go (about) 1 package jsonschema 2 3 import "fmt" 4 5 // EvaluateContains checks if at least one element in an array meets the conditions specified by the 'contains' keyword. 6 // It follows the JSON Schema Draft 2020-12: 7 // - "contains" must be associated with a valid JSON Schema. 8 // - An array is valid if at least one of its elements matches the given schema, unless "minContains" is 0. 9 // - When "minContains" is 0, the array is considered valid even if no elements match the schema. 10 // - Produces annotations of indexes where the schema validates successfully. 11 // - If every element validates, it produces a boolean "true". 12 // - If the array is empty, annotations reflect the validation state. 13 // - Influences "unevaluatedItems" and may be used to implement "minContains" and "maxContains". 14 // 15 // This function provides detailed feedback on element validation and gathers comprehensive annotations. 16 // 17 // Reference: https://json-schema.org/draft/2020-12/json-schema-core#name-contains 18 func evaluateContains(schema *Schema, data []interface{}, _ map[string]bool, evaluatedItems map[int]bool, dynamicScope *DynamicScope) ([]*EvaluationResult, *EvaluationError) { 19 if schema.Contains == nil { 20 // No 'contains' constraint is defined, skip further checks. 21 return nil, nil 22 } 23 24 results := []*EvaluationResult{} 25 26 var validCount int 27 for i, item := range data { 28 result, _, _ := schema.Contains.evaluate(item, dynamicScope) 29 30 if result != nil { 31 //nolint:errcheck 32 result.SetEvaluationPath("/contains"). 33 SetSchemaLocation(schema.GetSchemaLocation("/contains")). 34 SetInstanceLocation(fmt.Sprintf("/%d", i)) 35 36 if result.IsValid() { 37 validCount++ 38 evaluatedItems[i] = true // Mark this item as evaluated 39 } 40 } 41 } 42 43 // Handle 'minContains' logic 44 minContains := 1 // Default value if 'minContains' is not specified 45 if schema.MinContains != nil { 46 minContains = int(*schema.MinContains) 47 } 48 49 if minContains == 0 && validCount == 0 { 50 // Valid scenario when minContains is 0. Still need to check maxContains. 51 } else if validCount < minContains { 52 return results, NewEvaluationError("minContains", "contains_too_few_items", "Value should contain at least {min_contains} matching items", map[string]interface{}{ 53 "min_contains": minContains, 54 "count": validCount, 55 }) 56 } 57 58 // Handle 'maxContains' logic 59 if schema.MaxContains != nil && validCount > int(*schema.MaxContains) { 60 return results, NewEvaluationError("maxContains", "contains_too_many_items", "Value should contain no more than {max_contains} matching items", map[string]interface{}{ 61 "max_contains": *schema.MaxContains, 62 "count": validCount, 63 }) 64 } 65 66 return results, nil 67 }