github.com/kaptinlin/jsonschema@v0.4.6/enum.go (about) 1 package jsonschema 2 3 import ( 4 "fmt" 5 "reflect" 6 "strings" 7 ) 8 9 // valuesEqual checks if two values are equal, handling type conversions for numeric types 10 func valuesEqual(a, b interface{}) bool { 11 // Try direct comparison first 12 if reflect.DeepEqual(a, b) { 13 return true 14 } 15 16 // Handle numeric type conversions 17 va := reflect.ValueOf(a) 18 vb := reflect.ValueOf(b) 19 20 // If both are numeric, convert to float64 for comparison 21 if isNumeric(va) && isNumeric(vb) { 22 fa, ok1 := toFloat64(a) 23 fb, ok2 := toFloat64(b) 24 if ok1 && ok2 { 25 return fa == fb 26 } 27 } 28 29 return false 30 } 31 32 // isNumeric checks if a reflect.Value represents a numeric type 33 func isNumeric(v reflect.Value) bool { 34 switch v.Kind() { 35 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, 36 reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, 37 reflect.Float32, reflect.Float64: 38 return true 39 case reflect.Invalid, reflect.Bool, reflect.Uintptr, 40 reflect.Complex64, reflect.Complex128, reflect.Array, reflect.Chan, 41 reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, 42 reflect.Slice, reflect.String, reflect.Struct, reflect.UnsafePointer: 43 return false 44 default: 45 return false 46 } 47 } 48 49 // toFloat64 converts various numeric types to float64 50 func toFloat64(value interface{}) (float64, bool) { 51 switch v := value.(type) { 52 case int: 53 return float64(v), true 54 case int8: 55 return float64(v), true 56 case int16: 57 return float64(v), true 58 case int32: 59 return float64(v), true 60 case int64: 61 return float64(v), true 62 case uint: 63 return float64(v), true 64 case uint8: 65 return float64(v), true 66 case uint16: 67 return float64(v), true 68 case uint32: 69 return float64(v), true 70 case uint64: 71 return float64(v), true 72 case float32: 73 return float64(v), true 74 case float64: 75 return v, true 76 default: 77 return 0, false 78 } 79 } 80 81 // EvaluateEnum checks if the data's value matches one of the enumerated values specified in the schema. 82 // According to the JSON Schema Draft 2020-12: 83 // - The value of the "enum" keyword must be an array. 84 // - This array should have at least one element, and all elements should be unique. 85 // - An instance validates successfully against this keyword if its value is equal to one of the elements in the array. 86 // - Elements in the array might be of any type, including null. 87 // 88 // This method ensures that the data instance conforms to the enumerated values defined in the schema. 89 // If the instance does not match any of the enumerated values, it returns a EvaluationError detailing the allowed values. 90 // 91 // Reference: https://json-schema.org/draft/2020-12/json-schema-validation#name-enum 92 func evaluateEnum(schema *Schema, instance interface{}) *EvaluationError { 93 if len(schema.Enum) == 0 { 94 return nil // No enum values, so no validation needed 95 } 96 97 allowed := make([]string, 0, len(schema.Enum)) 98 99 for _, enumValue := range schema.Enum { 100 if valuesEqual(instance, enumValue) { 101 return nil // Match found. 102 } 103 104 allowed = append(allowed, fmt.Sprintf("%v", enumValue)) 105 } 106 107 // No match found. 108 return NewEvaluationError("enum", "value_not_in_enum", "Value {received} should be one of the allowed values: {expected}", map[string]interface{}{ 109 "expected": strings.Join(allowed, ", "), 110 "received": instance, 111 }) 112 }