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

     1  package jsonschema
     2  
     3  import (
     4  	"fmt"
     5  	"strconv"
     6  	"strings"
     7  )
     8  
     9  // EvaluateOneOf checks if the data conforms to exactly one of the schemas specified in the oneOf attribute.
    10  // According to the JSON Schema Draft 2020-12:
    11  //   - The "oneOf" keyword's value must be a non-empty array, where each item is either a valid JSON Schema or a boolean.
    12  //   - An instance validates successfully against this keyword if it validates successfully against exactly one schema or is true for exactly one boolean in this array.
    13  //
    14  // This function ensures that the data instance meets exactly one of the specified constraints defined by the schemas or booleans in the oneOf array.
    15  // If the instance conforms to more than one or none of the schemas, it returns a EvaluationError detailing the specific failures or the lack of a valid schema.
    16  //
    17  // Reference: https://json-schema.org/draft/2020-12/json-schema-core#name-oneof
    18  func evaluateOneOf(schema *Schema, instance interface{}, evaluatedProps map[string]bool, evaluatedItems map[int]bool, dynamicScope *DynamicScope) ([]*EvaluationResult, *EvaluationError) {
    19  	if len(schema.OneOf) == 0 {
    20  		return nil, nil // No oneOf constraints to validate against.
    21  	}
    22  
    23  	valid_indexs := []string{}
    24  	results := []*EvaluationResult{}
    25  	var tempEvaluatedProps map[string]bool
    26  	var tempEvaluatedItems map[int]bool
    27  
    28  	for i, subSchema := range schema.OneOf {
    29  		if subSchema != nil {
    30  			result, schemaEvaluatedProps, schemaEvaluatedItems := subSchema.evaluate(instance, dynamicScope)
    31  			if result != nil {
    32  				results = append(results, result.SetEvaluationPath(fmt.Sprintf("/oneOf/%d", i)).
    33  					SetSchemaLocation(schema.GetSchemaLocation(fmt.Sprintf("/oneOf/%d", i))).
    34  					SetInstanceLocation(""),
    35  				)
    36  
    37  				if result.IsValid() {
    38  					valid_indexs = append(valid_indexs, strconv.Itoa(i))
    39  					tempEvaluatedProps = schemaEvaluatedProps
    40  					tempEvaluatedItems = schemaEvaluatedItems
    41  				}
    42  			}
    43  		}
    44  	}
    45  
    46  	if len(valid_indexs) == 1 {
    47  		// Merge maps only if exactly one schema or boolean condition is successfully validated
    48  		mergeStringMaps(evaluatedProps, tempEvaluatedProps)
    49  		mergeIntMaps(evaluatedItems, tempEvaluatedItems)
    50  		return results, nil
    51  	}
    52  
    53  	if len(valid_indexs) > 1 {
    54  		return results, NewEvaluationError("oneOf", "one_of_multiple_matches", "Value should match exactly one schema but matches multiple at indexes {matches}", map[string]interface{}{
    55  			"matches": strings.Join(valid_indexs, ", "),
    56  		})
    57  	} else { // If no conditions are met, return error
    58  		return results, NewEvaluationError("oneOf", "one_of_item_mismatch", "Value does not match the oneOf schema")
    59  	}
    60  }