normalizeForJSONSchema recursively returns a normalized copy of v with YAML-native Go types converted to JSON-compatible types for JSON schema validation. It does not mutate the caller's maps or slices. goccy/go-yaml produces uint64 for positive integers and int64 for negative integers, but JSON sch
(v any)
| 220 | // the reflection fallback converts these so the schema validator sees []any. |
| 221 | // This avoids the overhead of a json.Marshal + json.Unmarshal roundtrip. |
| 222 | func normalizeForJSONSchema(v any) any { |
| 223 | switch val := v.(type) { |
| 224 | case map[string]any: |
| 225 | normalized := make(map[string]any, len(val)) |
| 226 | for k, elem := range val { |
| 227 | normalized[k] = normalizeForJSONSchema(elem) |
| 228 | } |
| 229 | return normalized |
| 230 | case []any: |
| 231 | normalized := make([]any, len(val)) |
| 232 | for i, elem := range val { |
| 233 | normalized[i] = normalizeForJSONSchema(elem) |
| 234 | } |
| 235 | return normalized |
| 236 | case int: |
| 237 | return float64(val) |
| 238 | case int8: |
| 239 | return float64(val) |
| 240 | case int16: |
| 241 | return float64(val) |
| 242 | case int32: |
| 243 | return float64(val) |
| 244 | case int64: |
| 245 | return float64(val) |
| 246 | case uint: |
| 247 | return float64(val) |
| 248 | case uint8: |
| 249 | return float64(val) |
| 250 | case uint16: |
| 251 | return float64(val) |
| 252 | case uint32: |
| 253 | return float64(val) |
| 254 | case uint64: |
| 255 | return float64(val) |
| 256 | case float32: |
| 257 | return float64(val) |
| 258 | default: |
| 259 | // Use reflection to handle typed slices (e.g. []string) and typed maps |
| 260 | // that goccy/go-yaml may produce instead of []any / map[string]any. |
| 261 | rv := reflect.ValueOf(v) |
| 262 | switch rv.Kind() { |
| 263 | case reflect.Slice: |
| 264 | normalized := make([]any, rv.Len()) |
| 265 | for i := range rv.Len() { |
| 266 | normalized[i] = normalizeForJSONSchema(rv.Index(i).Interface()) |
| 267 | } |
| 268 | return normalized |
| 269 | case reflect.Map: |
| 270 | normalized := make(map[string]any, rv.Len()) |
| 271 | for _, key := range rv.MapKeys() { |
| 272 | normalized[key.String()] = normalizeForJSONSchema(rv.MapIndex(key).Interface()) |
| 273 | } |
| 274 | return normalized |
| 275 | } |
| 276 | // string, bool, float64, nil pass through unchanged |
| 277 | return v |
| 278 | } |
| 279 | } |