github.com/kaptinlin/jsonschema@v0.4.6/additionalProperties.go (about) 1 package jsonschema 2 3 import ( 4 "fmt" 5 "strings" 6 ) 7 8 // EvaluateAdditionalProperties checks if properties not explicitly defined or matched by patternProperties conform to the schema specified in additionalProperties. 9 // According to the JSON Schema Draft 2020-12: 10 // - The value of "additionalProperties" must be a valid JSON Schema. 11 // - This keyword validates child values of instance names that do not appear in the annotation results of either "properties" or "patternProperties". 12 // - Validation succeeds for these properties if the child instance validates against the "additionalProperties" schema. 13 // - Omitting "additionalProperties" has the same assertion behavior as an empty schema, which allows any type of value. 14 // 15 // This function ensures that all properties not explicitly mentioned or matched are validated according to a default schema or constraints. 16 // 17 // Reference: https://json-schema.org/draft/2020-12/json-schema-core#name-additionalproperties 18 func evaluateAdditionalProperties(schema *Schema, object map[string]interface{}, evaluatedProps map[string]bool, _ map[int]bool, dynamicScope *DynamicScope) ([]*EvaluationResult, *EvaluationError) { 19 results := []*EvaluationResult{} 20 invalid_properties := []string{} 21 22 properties := make(map[string]bool) 23 if schema.Properties != nil { 24 for propName := range *schema.Properties { 25 properties[propName] = true 26 } 27 } 28 if schema.PatternProperties != nil { 29 for _, regex := range schema.compiledPatterns { 30 for propName := range object { 31 if regex.MatchString(propName) { 32 properties[propName] = true 33 } 34 } 35 } 36 } 37 38 // Evaluate additional properties 39 if schema.AdditionalProperties != nil { 40 for propName, propValue := range object { 41 if !properties[propName] { 42 result, _, _ := schema.AdditionalProperties.evaluate(propValue, dynamicScope) 43 if result != nil { 44 //nolint:errcheck 45 result.SetEvaluationPath(fmt.Sprintf("/additionalProperties/%s", propName)). 46 SetSchemaLocation(schema.GetSchemaLocation(fmt.Sprintf("/additionalProperties/%s", propName))). 47 SetInstanceLocation(fmt.Sprintf("/%s", propName)) 48 49 results = append(results, result) 50 if !result.IsValid() { 51 invalid_properties = append(invalid_properties, propName) 52 } 53 } 54 55 // Mark property as evaluated 56 evaluatedProps[propName] = true 57 } 58 } 59 } 60 61 if len(invalid_properties) == 1 { 62 return results, NewEvaluationError("additionalProperties", "additional_property_mismatch", "Additional property {property} does not match the schema", map[string]interface{}{ 63 "property": fmt.Sprintf("'%s'", invalid_properties[0]), 64 }) 65 } else if len(invalid_properties) > 1 { 66 quotedProperties := make([]string, len(invalid_properties)) 67 for i, prop := range invalid_properties { 68 quotedProperties[i] = fmt.Sprintf("'%s'", prop) 69 } 70 return results, NewEvaluationError("additionalProperties", "additional_properties_mismatch", "Additional properties {properties} do not match the schema", map[string]interface{}{ 71 "properties": strings.Join(quotedProperties, ", "), 72 }) 73 } 74 75 return results, nil 76 }