github.com/kaptinlin/jsonschema@v0.4.6/dependentSchemas.go (about)

     1  package jsonschema
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  )
     7  
     8  // EvaluateDependentSchemas checks if the data conforms to dependent schemas specified in the 'dependentSchemas' attribute.
     9  // According to the JSON Schema Draft 2020-12:
    10  //   - The "dependentSchemas" keyword's value must be an object, where each value is a valid JSON Schema.
    11  //   - This validation ensures that if a specific property is present in the instance, then the entire instance must validate against the associated schema.
    12  //
    13  // This function ensures that the instance meets the conditional constraints defined by the dependent schemas.
    14  // If the instance fails to conform to any dependent schema when the associated property is present, it returns a EvaluationError.
    15  //
    16  // Reference: https://json-schema.org/draft/2020-12/json-schema-core#name-dependentschemas
    17  func evaluateDependentSchemas(schema *Schema, instance interface{}, evaluatedProps map[string]bool, evaluatedItems map[int]bool, dynamicScope *DynamicScope) ([]*EvaluationResult, *EvaluationError) {
    18  	if len(schema.DependentSchemas) == 0 {
    19  		return nil, nil // No dependentSchemas constraints to validate against.
    20  	}
    21  
    22  	object, ok := instance.(map[string]interface{})
    23  	if !ok {
    24  		return nil, nil // instance is not an object, dependentSchemas do not apply.
    25  	}
    26  	invalid_properties := []string{}
    27  	results := []*EvaluationResult{}
    28  
    29  	for propName, depSchema := range schema.DependentSchemas {
    30  		if _, exists := object[propName]; exists {
    31  			if depSchema != nil {
    32  				result, schemaEvaluatedProps, schemaEvaluatedItems := depSchema.evaluate(object, dynamicScope)
    33  				if result != nil {
    34  					//nolint:errcheck
    35  					result.SetEvaluationPath(fmt.Sprintf("/dependentSchemas/%s", propName)).
    36  						SetSchemaLocation(schema.GetSchemaLocation(fmt.Sprintf("/dependentSchemas/%s", propName))).
    37  						SetInstanceLocation(fmt.Sprintf("/%s", propName))
    38  				}
    39  
    40  				if result.IsValid() {
    41  					// Merge maps only if dependent schema validation is successful
    42  					mergeStringMaps(evaluatedProps, schemaEvaluatedProps)
    43  					mergeIntMaps(evaluatedItems, schemaEvaluatedItems)
    44  				} else {
    45  					invalid_properties = append(invalid_properties, propName)
    46  				}
    47  			}
    48  		}
    49  	}
    50  
    51  	if len(invalid_properties) == 1 {
    52  		return results, NewEvaluationError("dependentSchemas", "dependent_schema_mismatch", "Property {property} does not match the dependent schema", map[string]interface{}{
    53  			"property": fmt.Sprintf("'%s'", invalid_properties[0]),
    54  		})
    55  	} else if len(invalid_properties) > 1 {
    56  		quotedProperties := make([]string, len(invalid_properties))
    57  		for i, prop := range invalid_properties {
    58  			quotedProperties[i] = fmt.Sprintf("'%s'", prop)
    59  		}
    60  		return results, NewEvaluationError("dependentSchemas", "dependent_schemas_mismatch", "Properties {properties} do not match the dependent schemas", map[string]interface{}{
    61  			"properties": strings.Join(quotedProperties, ", "),
    62  		})
    63  	}
    64  
    65  	return results, nil
    66  }