github.com/kaptinlin/jsonschema@v0.4.6/validate.go (about) 1 package jsonschema 2 3 import ( 4 "reflect" 5 ) 6 7 // Validate checks if the given instance conforms to the schema. 8 // This method automatically detects the input type and delegates to the appropriate validation method. 9 func (s *Schema) Validate(instance interface{}) *EvaluationResult { 10 switch data := instance.(type) { 11 case []byte: 12 return s.ValidateJSON(data) 13 case map[string]interface{}: 14 return s.ValidateMap(data) 15 default: 16 return s.ValidateStruct(instance) 17 } 18 } 19 20 // ValidateJSON validates JSON data provided as []byte. 21 // The input is guaranteed to be treated as JSON data and parsed accordingly. 22 func (s *Schema) ValidateJSON(data []byte) *EvaluationResult { 23 parsed, err := s.parseJSONData(data) 24 if err != nil { 25 result := NewEvaluationResult(s) 26 //nolint:errcheck 27 result.AddError(NewEvaluationError("format", "invalid_json", "Invalid JSON format")) 28 return result 29 } 30 31 dynamicScope := NewDynamicScope() 32 result, _, _ := s.evaluate(parsed, dynamicScope) 33 return result 34 } 35 36 // ValidateStruct validates Go struct data directly using reflection. 37 // This method uses cached reflection data for optimal performance. 38 func (s *Schema) ValidateStruct(instance interface{}) *EvaluationResult { 39 dynamicScope := NewDynamicScope() 40 result, _, _ := s.evaluate(instance, dynamicScope) 41 return result 42 } 43 44 // ValidateMap validates map[string]interface{} data directly. 45 // This method provides optimal performance for pre-parsed JSON data. 46 func (s *Schema) ValidateMap(data map[string]interface{}) *EvaluationResult { 47 dynamicScope := NewDynamicScope() 48 result, _, _ := s.evaluate(data, dynamicScope) 49 return result 50 } 51 52 // parseJSONData safely parses []byte data as JSON 53 func (s *Schema) parseJSONData(data []byte) (interface{}, error) { 54 var parsed interface{} 55 return parsed, s.GetCompiler().jsonDecoder(data, &parsed) 56 } 57 58 // processJSONBytes handles []byte input with smart JSON parsing 59 func (s *Schema) processJSONBytes(jsonBytes []byte) (interface{}, error) { 60 var parsed interface{} 61 if err := s.GetCompiler().jsonDecoder(jsonBytes, &parsed); err == nil { 62 return parsed, nil 63 } 64 65 // Only return error if it looks like intended JSON 66 if len(jsonBytes) > 0 && (jsonBytes[0] == '{' || jsonBytes[0] == '[') { 67 return nil, s.GetCompiler().jsonDecoder(jsonBytes, &parsed) 68 } 69 70 // Otherwise, keep original bytes for validation as byte array 71 return jsonBytes, nil 72 } 73 74 func (s *Schema) evaluate(instance interface{}, dynamicScope *DynamicScope) (*EvaluationResult, map[string]bool, map[int]bool) { 75 // Handle []byte input 76 instance = s.preprocessByteInput(instance) 77 78 dynamicScope.Push(s) 79 defer dynamicScope.Pop() 80 81 result := NewEvaluationResult(s) 82 evaluatedProps := make(map[string]bool) 83 evaluatedItems := make(map[int]bool) 84 85 // Process schema types 86 if s.Boolean != nil { 87 if err := s.evaluateBoolean(instance, evaluatedProps, evaluatedItems); err != nil { 88 //nolint:errcheck 89 result.AddError(err) 90 } 91 return result, evaluatedProps, evaluatedItems 92 } 93 94 // Compile patterns if needed 95 if s.PatternProperties != nil { 96 s.compilePatterns() 97 } 98 99 // Process references 100 s.processReferences(instance, dynamicScope, result, evaluatedProps, evaluatedItems) 101 102 // Process validation keywords 103 s.processValidationKeywords(instance, dynamicScope, result, evaluatedProps, evaluatedItems) 104 105 return result, evaluatedProps, evaluatedItems 106 } 107 108 // preprocessByteInput handles []byte input intelligently 109 func (s *Schema) preprocessByteInput(instance interface{}) interface{} { 110 jsonBytes, ok := instance.([]byte) 111 if !ok { 112 return instance 113 } 114 115 parsed, err := s.processJSONBytes(jsonBytes) 116 if err != nil { 117 // Create a temporary result to hold the JSON parsing error 118 // Return the error as part of the instance for downstream handling 119 return &jsonParseError{data: jsonBytes, err: err} 120 } 121 122 return parsed 123 } 124 125 // jsonParseError wraps JSON parsing errors for downstream handling 126 type jsonParseError struct { 127 data []byte 128 err error 129 } 130 131 // processReferences handles $ref and $dynamicRef evaluation 132 func (s *Schema) processReferences(instance interface{}, dynamicScope *DynamicScope, result *EvaluationResult, evaluatedProps map[string]bool, evaluatedItems map[int]bool) { 133 // Handle JSON parse errors 134 if _, ok := instance.(*jsonParseError); ok { 135 //nolint:errcheck 136 result.AddError(NewEvaluationError("format", "invalid_json", "Invalid JSON format in byte array")) 137 return 138 } 139 140 // Process $ref 141 if s.ResolvedRef != nil { 142 refResult, props, items := s.ResolvedRef.evaluate(instance, dynamicScope) 143 if refResult != nil { 144 //nolint:errcheck 145 result.AddDetail(refResult) 146 if !refResult.IsValid() { 147 //nolint:errcheck 148 result.AddError(NewEvaluationError("$ref", "ref_mismatch", "Value does not match the reference schema")) 149 } 150 } 151 mergeStringMaps(evaluatedProps, props) 152 mergeIntMaps(evaluatedItems, items) 153 } 154 155 // Process $dynamicRef 156 if s.ResolvedDynamicRef != nil { 157 s.processDynamicRef(instance, dynamicScope, result, evaluatedProps, evaluatedItems) 158 } 159 } 160 161 // processDynamicRef handles $dynamicRef evaluation 162 func (s *Schema) processDynamicRef(instance interface{}, dynamicScope *DynamicScope, result *EvaluationResult, evaluatedProps map[string]bool, evaluatedItems map[int]bool) { 163 anchorSchema := s.ResolvedDynamicRef 164 _, anchor := splitRef(s.DynamicRef) 165 166 if !isJSONPointer(anchor) { 167 if dynamicAnchor := s.ResolvedDynamicRef.DynamicAnchor; dynamicAnchor != "" { 168 if schema := dynamicScope.LookupDynamicAnchor(dynamicAnchor); schema != nil { 169 anchorSchema = schema 170 } 171 } 172 } 173 174 dynamicRefResult, props, items := anchorSchema.evaluate(instance, dynamicScope) 175 if dynamicRefResult != nil { 176 //nolint:errcheck 177 result.AddDetail(dynamicRefResult) 178 if !dynamicRefResult.IsValid() { 179 //nolint:errcheck 180 result.AddError(NewEvaluationError("$dynamicRef", "dynamic_ref_mismatch", "Value does not match the dynamic reference schema")) 181 } 182 } 183 184 mergeStringMaps(evaluatedProps, props) 185 mergeIntMaps(evaluatedItems, items) 186 } 187 188 // processValidationKeywords handles all validation keywords 189 func (s *Schema) processValidationKeywords(instance interface{}, dynamicScope *DynamicScope, result *EvaluationResult, evaluatedProps map[string]bool, evaluatedItems map[int]bool) { 190 // Basic type validation 191 s.processBasicValidation(instance, result) 192 193 // Logical operations 194 s.processLogicalOperations(instance, dynamicScope, result, evaluatedProps, evaluatedItems) 195 196 // Conditional logic 197 s.processConditionalLogic(instance, dynamicScope, result, evaluatedProps, evaluatedItems) 198 199 // Type-specific validation 200 s.processTypeSpecificValidation(instance, dynamicScope, result, evaluatedProps, evaluatedItems) 201 202 // Content validation 203 s.processContentValidation(instance, dynamicScope, result, evaluatedProps, evaluatedItems) 204 } 205 206 // processBasicValidation handles basic validation keywords 207 func (s *Schema) processBasicValidation(instance interface{}, result *EvaluationResult) { 208 if s.Type != nil { 209 if err := evaluateType(s, instance); err != nil { 210 //nolint:errcheck 211 result.AddError(err) 212 } 213 } 214 215 if s.Enum != nil { 216 if err := evaluateEnum(s, instance); err != nil { 217 //nolint:errcheck 218 result.AddError(err) 219 } 220 } 221 222 if s.Const != nil { 223 if err := evaluateConst(s, instance); err != nil { 224 //nolint:errcheck 225 result.AddError(err) 226 } 227 } 228 } 229 230 // processLogicalOperations handles allOf, anyOf, oneOf, not 231 func (s *Schema) processLogicalOperations(instance interface{}, dynamicScope *DynamicScope, result *EvaluationResult, evaluatedProps map[string]bool, evaluatedItems map[int]bool) { 232 if s.AllOf != nil { 233 results, err := evaluateAllOf(s, instance, evaluatedProps, evaluatedItems, dynamicScope) 234 s.addResultsAndError(result, results, err) 235 } 236 237 if s.AnyOf != nil { 238 results, err := evaluateAnyOf(s, instance, evaluatedProps, evaluatedItems, dynamicScope) 239 s.addResultsAndError(result, results, err) 240 } 241 242 if s.OneOf != nil { 243 results, err := evaluateOneOf(s, instance, evaluatedProps, evaluatedItems, dynamicScope) 244 s.addResultsAndError(result, results, err) 245 } 246 247 if s.Not != nil { 248 evalResult, err := evaluateNot(s, instance, evaluatedProps, evaluatedItems, dynamicScope) 249 if evalResult != nil { 250 //nolint:errcheck 251 result.AddDetail(evalResult) 252 } 253 if err != nil { 254 //nolint:errcheck 255 result.AddError(err) 256 } 257 } 258 } 259 260 // processConditionalLogic handles if/then/else 261 func (s *Schema) processConditionalLogic(instance interface{}, dynamicScope *DynamicScope, result *EvaluationResult, evaluatedProps map[string]bool, evaluatedItems map[int]bool) { 262 if s.If != nil || s.Then != nil || s.Else != nil { 263 results, err := evaluateConditional(s, instance, evaluatedProps, evaluatedItems, dynamicScope) 264 s.addResultsAndError(result, results, err) 265 } 266 } 267 268 // processTypeSpecificValidation handles array, object, string, and numeric validation 269 func (s *Schema) processTypeSpecificValidation(instance interface{}, dynamicScope *DynamicScope, result *EvaluationResult, evaluatedProps map[string]bool, evaluatedItems map[int]bool) { 270 // Array validation 271 if s.hasArrayValidation() { 272 results, errors := evaluateArray(s, instance, evaluatedProps, evaluatedItems, dynamicScope) 273 s.addResultsAndErrors(result, results, errors) 274 } 275 276 // Numeric validation 277 if s.hasNumericValidation() { 278 errors := evaluateNumeric(s, instance) 279 s.addErrors(result, errors) 280 } 281 282 // String validation 283 if s.hasStringValidation() { 284 errors := evaluateString(s, instance) 285 s.addErrors(result, errors) 286 } 287 288 if s.Format != nil { 289 if err := evaluateFormat(s, instance); err != nil { 290 //nolint:errcheck 291 result.AddError(err) 292 } 293 } 294 295 // Object validation 296 if s.hasObjectValidation() { 297 results, errors := evaluateObject(s, instance, evaluatedProps, evaluatedItems, dynamicScope) 298 s.addResultsAndErrors(result, results, errors) 299 } 300 301 // Dependent schemas 302 if s.DependentSchemas != nil { 303 results, err := evaluateDependentSchemas(s, instance, evaluatedProps, evaluatedItems, dynamicScope) 304 s.addResultsAndError(result, results, err) 305 } 306 307 // Unevaluated properties and items 308 s.processUnevaluatedValidation(instance, dynamicScope, result, evaluatedProps, evaluatedItems) 309 } 310 311 // processContentValidation handles content encoding/media type/schema 312 func (s *Schema) processContentValidation(instance interface{}, dynamicScope *DynamicScope, result *EvaluationResult, evaluatedProps map[string]bool, evaluatedItems map[int]bool) { 313 if s.ContentEncoding != nil || s.ContentMediaType != nil || s.ContentSchema != nil { 314 contentResult, err := evaluateContent(s, instance, evaluatedProps, evaluatedItems, dynamicScope) 315 if contentResult != nil { 316 //nolint:errcheck 317 result.AddDetail(contentResult) 318 } 319 if err != nil { 320 //nolint:errcheck 321 result.AddError(err) 322 } 323 } 324 } 325 326 // processUnevaluatedValidation handles unevaluated properties and items 327 func (s *Schema) processUnevaluatedValidation(instance interface{}, dynamicScope *DynamicScope, result *EvaluationResult, evaluatedProps map[string]bool, evaluatedItems map[int]bool) { 328 if s.UnevaluatedProperties != nil { 329 results, err := evaluateUnevaluatedProperties(s, instance, evaluatedProps, evaluatedItems, dynamicScope) 330 s.addResultsAndError(result, results, err) 331 } 332 333 if s.UnevaluatedItems != nil { 334 results, err := evaluateUnevaluatedItems(s, instance, evaluatedProps, evaluatedItems, dynamicScope) 335 s.addResultsAndError(result, results, err) 336 } 337 } 338 339 // Helper methods for checking if schema has specific validation types 340 func (s *Schema) hasArrayValidation() bool { 341 return len(s.PrefixItems) > 0 || s.Items != nil || s.Contains != nil || 342 s.MaxContains != nil || s.MinContains != nil || s.MaxItems != nil || 343 s.MinItems != nil || s.UniqueItems != nil 344 } 345 346 func (s *Schema) hasNumericValidation() bool { 347 return s.MultipleOf != nil || s.Maximum != nil || s.ExclusiveMaximum != nil || 348 s.Minimum != nil || s.ExclusiveMinimum != nil 349 } 350 351 func (s *Schema) hasStringValidation() bool { 352 return s.MaxLength != nil || s.MinLength != nil || s.Pattern != nil 353 } 354 355 func (s *Schema) hasObjectValidation() bool { 356 return s.Properties != nil || s.PatternProperties != nil || s.AdditionalProperties != nil || 357 s.PropertyNames != nil || s.MaxProperties != nil || s.MinProperties != nil || 358 len(s.Required) > 0 || len(s.DependentRequired) > 0 359 } 360 361 // Helper methods for adding results and errors 362 func (s *Schema) addResultsAndError(result *EvaluationResult, results []*EvaluationResult, err *EvaluationError) { 363 for _, res := range results { 364 //nolint:errcheck 365 result.AddDetail(res) 366 } 367 if err != nil { 368 //nolint:errcheck 369 result.AddError(err) 370 } 371 } 372 373 func (s *Schema) addResultsAndErrors(result *EvaluationResult, results []*EvaluationResult, errors []*EvaluationError) { 374 for _, res := range results { 375 //nolint:errcheck 376 result.AddDetail(res) 377 } 378 s.addErrors(result, errors) 379 } 380 381 func (s *Schema) addErrors(result *EvaluationResult, errors []*EvaluationError) { 382 for _, err := range errors { 383 //nolint:errcheck 384 result.AddError(err) 385 } 386 } 387 388 func (s *Schema) evaluateBoolean(instance interface{}, evaluatedProps map[string]bool, evaluatedItems map[int]bool) *EvaluationError { 389 if s.Boolean == nil { 390 return nil 391 } 392 393 if *s.Boolean { 394 switch v := instance.(type) { 395 case map[string]interface{}: 396 for key := range v { 397 evaluatedProps[key] = true 398 } 399 case []interface{}: 400 for index := range v { 401 evaluatedItems[index] = true 402 } 403 } 404 return nil 405 } 406 407 return NewEvaluationError("schema", "false_schema_mismatch", "No values are allowed because the schema is set to 'false'") 408 } 409 410 // evaluateObject groups the validation of all object-specific keywords. 411 func evaluateObject(schema *Schema, data interface{}, evaluatedProps map[string]bool, evaluatedItems map[int]bool, dynamicScope *DynamicScope) ([]*EvaluationResult, []*EvaluationError) { 412 // Fast path: direct map[string]interface{} type 413 if object, ok := data.(map[string]interface{}); ok { 414 return evaluateObjectMap(schema, object, evaluatedProps, evaluatedItems, dynamicScope) 415 } 416 417 // Reflection path: handle structs and other object types 418 rv := reflect.ValueOf(data) 419 for rv.Kind() == reflect.Ptr { 420 if rv.IsNil() { 421 return nil, nil 422 } 423 rv = rv.Elem() 424 } 425 426 //nolint:exhaustive // Only handling Struct and Map kinds - other types use default fallback 427 switch rv.Kind() { 428 case reflect.Struct: 429 return evaluateObjectStruct(schema, rv, evaluatedProps, evaluatedItems, dynamicScope) 430 case reflect.Map: 431 if rv.Type().Key().Kind() == reflect.String { 432 return evaluateObjectReflectMap(schema, rv, evaluatedProps, evaluatedItems, dynamicScope) 433 } 434 default: 435 // Handle other kinds by returning nil 436 } 437 438 return nil, nil 439 } 440 441 // evaluateObjectMap handles validation for map[string]interface{} (original implementation) 442 func evaluateObjectMap(schema *Schema, object map[string]interface{}, evaluatedProps map[string]bool, evaluatedItems map[int]bool, dynamicScope *DynamicScope) ([]*EvaluationResult, []*EvaluationError) { 443 var results []*EvaluationResult 444 var errors []*EvaluationError 445 446 // Properties validation 447 if schema.Properties != nil { 448 if propResults, propError := evaluateProperties(schema, object, evaluatedProps, evaluatedItems, dynamicScope); propResults != nil || propError != nil { 449 results = append(results, propResults...) 450 if propError != nil { 451 errors = append(errors, propError) 452 } 453 } 454 } 455 456 // Pattern properties validation 457 if schema.PatternProperties != nil { 458 if patResults, patError := evaluatePatternProperties(schema, object, evaluatedProps, evaluatedItems, dynamicScope); patResults != nil || patError != nil { 459 results = append(results, patResults...) 460 if patError != nil { 461 errors = append(errors, patError) 462 } 463 } 464 } 465 466 // Additional properties validation 467 if schema.AdditionalProperties != nil { 468 if addResults, addError := evaluateAdditionalProperties(schema, object, evaluatedProps, evaluatedItems, dynamicScope); addResults != nil || addError != nil { 469 results = append(results, addResults...) 470 if addError != nil { 471 errors = append(errors, addError) 472 } 473 } 474 } 475 476 // Property names validation 477 if schema.PropertyNames != nil { 478 if nameResults, nameError := evaluatePropertyNames(schema, object, evaluatedProps, evaluatedItems, dynamicScope); nameResults != nil || nameError != nil { 479 results = append(results, nameResults...) 480 if nameError != nil { 481 errors = append(errors, nameError) 482 } 483 } 484 } 485 486 // Object constraint validation 487 errors = append(errors, validateObjectConstraints(schema, object)...) 488 489 return results, errors 490 } 491 492 // validateObjectConstraints validates object-specific constraints 493 func validateObjectConstraints(schema *Schema, object map[string]interface{}) []*EvaluationError { 494 var errors []*EvaluationError 495 496 if schema.MaxProperties != nil { 497 if err := evaluateMaxProperties(schema, object); err != nil { 498 errors = append(errors, err) 499 } 500 } 501 502 if schema.MinProperties != nil { 503 if err := evaluateMinProperties(schema, object); err != nil { 504 errors = append(errors, err) 505 } 506 } 507 508 if len(schema.Required) > 0 { 509 if err := evaluateRequired(schema, object); err != nil { 510 errors = append(errors, err) 511 } 512 } 513 514 if len(schema.DependentRequired) > 0 { 515 if err := evaluateDependentRequired(schema, object); err != nil { 516 errors = append(errors, err) 517 } 518 } 519 520 return errors 521 } 522 523 // validateNumeric groups the validation of all numeric-specific keywords. 524 func evaluateNumeric(schema *Schema, data interface{}) []*EvaluationError { 525 dataType := getDataType(data) 526 if dataType != "number" && dataType != "integer" { 527 return nil 528 } 529 530 value := NewRat(data) 531 if value == nil { 532 return []*EvaluationError{ 533 NewEvaluationError("type", "invalid_numberic", "Value is {received} but should be numeric", map[string]interface{}{ 534 "actual_type": dataType, 535 }), 536 } 537 } 538 539 var errors []*EvaluationError 540 541 // Collect all numeric validation errors 542 if schema.MultipleOf != nil { 543 if err := evaluateMultipleOf(schema, value); err != nil { 544 errors = append(errors, err) 545 } 546 } 547 548 if schema.Maximum != nil { 549 if err := evaluateMaximum(schema, value); err != nil { 550 errors = append(errors, err) 551 } 552 } 553 554 if schema.ExclusiveMaximum != nil { 555 if err := evaluateExclusiveMaximum(schema, value); err != nil { 556 errors = append(errors, err) 557 } 558 } 559 560 if schema.Minimum != nil { 561 if err := evaluateMinimum(schema, value); err != nil { 562 errors = append(errors, err) 563 } 564 } 565 566 if schema.ExclusiveMinimum != nil { 567 if err := evaluateExclusiveMinimum(schema, value); err != nil { 568 errors = append(errors, err) 569 } 570 } 571 572 return errors 573 } 574 575 // validateString groups the validation of all string-specific keywords. 576 func evaluateString(schema *Schema, data interface{}) []*EvaluationError { 577 value, ok := data.(string) 578 if !ok { 579 return nil 580 } 581 582 var errors []*EvaluationError 583 584 // Collect all string validation errors 585 if schema.MaxLength != nil { 586 if err := evaluateMaxLength(schema, value); err != nil { 587 errors = append(errors, err) 588 } 589 } 590 591 if schema.MinLength != nil { 592 if err := evaluateMinLength(schema, value); err != nil { 593 errors = append(errors, err) 594 } 595 } 596 597 if schema.Pattern != nil { 598 if err := evaluatePattern(schema, value); err != nil { 599 errors = append(errors, err) 600 } 601 } 602 603 return errors 604 } 605 606 // validateArray groups the validation of all array-specific keywords. 607 func evaluateArray(schema *Schema, data interface{}, evaluatedProps map[string]bool, evaluatedItems map[int]bool, dynamicScope *DynamicScope) ([]*EvaluationResult, []*EvaluationError) { 608 items, ok := data.([]interface{}) 609 if !ok { 610 return nil, nil 611 } 612 613 var results []*EvaluationResult 614 var errors []*EvaluationError 615 616 // Process array schema validations 617 arrayValidations := []func(*Schema, []interface{}, map[string]bool, map[int]bool, *DynamicScope) ([]*EvaluationResult, *EvaluationError){ 618 evaluatePrefixItems, 619 evaluateItems, 620 evaluateContains, 621 } 622 623 for _, validate := range arrayValidations { 624 if res, err := validate(schema, items, evaluatedProps, evaluatedItems, dynamicScope); res != nil || err != nil { 625 if res != nil { 626 results = append(results, res...) 627 } 628 if err != nil { 629 errors = append(errors, err) 630 } 631 } 632 } 633 634 // Array constraint validation 635 errors = append(errors, validateArrayConstraints(schema, items)...) 636 637 return results, errors 638 } 639 640 // validateArrayConstraints validates array-specific constraints 641 func validateArrayConstraints(schema *Schema, items []interface{}) []*EvaluationError { 642 var errors []*EvaluationError 643 644 if schema.MaxItems != nil { 645 if err := evaluateMaxItems(schema, items); err != nil { 646 errors = append(errors, err) 647 } 648 } 649 650 if schema.MinItems != nil { 651 if err := evaluateMinItems(schema, items); err != nil { 652 errors = append(errors, err) 653 } 654 } 655 656 if schema.UniqueItems != nil && *schema.UniqueItems { 657 if err := evaluateUniqueItems(schema, items); err != nil { 658 errors = append(errors, err) 659 } 660 } 661 662 return errors 663 } 664 665 // DynamicScope struct defines a stack specifically for handling Schema types 666 type DynamicScope struct { 667 schemas []*Schema // Slice storing pointers to Schema 668 } 669 670 // NewDynamicScope creates and returns a new empty DynamicScope 671 func NewDynamicScope() *DynamicScope { 672 return &DynamicScope{schemas: make([]*Schema, 0)} 673 } 674 675 // Push adds a Schema to the dynamic scope 676 func (ds *DynamicScope) Push(schema *Schema) { 677 ds.schemas = append(ds.schemas, schema) 678 } 679 680 // Pop removes and returns the top Schema from the dynamic scope 681 func (ds *DynamicScope) Pop() *Schema { 682 if len(ds.schemas) == 0 { 683 return nil 684 } 685 lastIndex := len(ds.schemas) - 1 686 schema := ds.schemas[lastIndex] 687 ds.schemas = ds.schemas[:lastIndex] 688 return schema 689 } 690 691 // Peek returns the top Schema without removing it 692 func (ds *DynamicScope) Peek() *Schema { 693 if len(ds.schemas) == 0 { 694 return nil 695 } 696 return ds.schemas[len(ds.schemas)-1] 697 } 698 699 // IsEmpty checks if the dynamic scope is empty 700 func (ds *DynamicScope) IsEmpty() bool { 701 return len(ds.schemas) == 0 702 } 703 704 // Size returns the number of Schemas in the dynamic scope 705 func (ds *DynamicScope) Size() int { 706 return len(ds.schemas) 707 } 708 709 // LookupDynamicAnchor searches for a dynamic anchor in the dynamic scope 710 func (ds *DynamicScope) LookupDynamicAnchor(anchor string) *Schema { 711 // use the first schema dynamic anchor matching the anchor 712 for i := 0; i < len(ds.schemas); i++ { 713 schema := ds.schemas[i] 714 715 if schema.dynamicAnchors != nil && schema.dynamicAnchors[anchor] != nil { 716 return schema.dynamicAnchors[anchor] 717 } 718 } 719 720 return nil 721 }