jsonschema: dynamic references
A dynamic reference is like a dynamic variable in a programming language, with two twists: - There must be a dynamic anchor in lexical scope for the ref to resolve. (Think of it as declaring the dynamic variable.) - The ref resolves to the schema highest on the stack, not lowest. That feels odd, but it turns out to work for the intended purpose, which is to provide extension points for additional features. There is some distracting code motion and refactoring in this CL: resolveRefs was moved down in the file, and resolution of a single ref was split off. Change-Id: I17da6052e0782fc540d14b8a41b06a2645923872 Reviewed-on: https://go-review.googlesource.com/c/tools/+/674155 Auto-Submit: Jonathan Amsterdam <jba@google.com> Reviewed-by: Alan Donovan <adonovan@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
parent
172901f3ac
commit
2a09582fa4
1 changed files with 1163 additions and 101 deletions
|
|
@ -8,6 +8,7 @@ package jsonschema
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"cmp"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
|
@ -110,15 +111,35 @@ type Schema struct {
|
|||
DependentSchemas map[string]*Schema `json:"dependentSchemas,omitempty"`
|
||||
|
||||
// computed fields
|
||||
// If the schema doesn't have an ID, the base URI is that of its parent.
|
||||
// Otherwise, the base URI is the ID resolved against the parent's baseURI.
|
||||
// The parent base URI at top level is where the schema was loaded from, or
|
||||
// if not loaded, then it should be provided to Schema.Resolve.
|
||||
baseURI *url.URL
|
||||
|
||||
// This schema's base schema.
|
||||
// If the schema is the root or has an ID, its base is itself.
|
||||
// Otherwise, its base is the innermost enclosing schema whose base
|
||||
// is itself.
|
||||
// Intuitively, a base schema is one that can be referred to with a
|
||||
// fragmentless URI.
|
||||
base *Schema
|
||||
|
||||
// The URI for the schema, if it is the root or has an ID.
|
||||
// Otherwise nil.
|
||||
// Invariants:
|
||||
// s.base.uri != nil.
|
||||
// s.base == s <=> s.uri != nil
|
||||
uri *url.URL
|
||||
|
||||
// The schema to which Ref refers.
|
||||
resolvedRef *Schema
|
||||
// map from anchors to subschemas
|
||||
anchors map[string]*Schema
|
||||
|
||||
// If the schema has a dynamic ref, exactly one of the next two fields
|
||||
// will be non-zero after successful resolution.
|
||||
// The schema to which the dynamic ref refers when it acts lexically.
|
||||
resolvedDynamicRef *Schema
|
||||
// The anchor to look up on the stack when the dynamic ref acts dynamically.
|
||||
dynamicRefAnchor string
|
||||
|
||||
// Map from anchors to subschemas.
|
||||
anchors map[string]anchorInfo
|
||||
|
||||
// compiled regexps
|
||||
pattern *regexp.Regexp
|
||||
patternProperties map[*regexp.Regexp]*Schema
|
||||
|
|
@ -129,10 +150,20 @@ func falseSchema() *Schema {
|
|||
return &Schema{Not: &Schema{}}
|
||||
}
|
||||
|
||||
// anchorInfo records the subschema to which an anchor refers, and whether
|
||||
// the anchor keyword is $anchor or $dynamicAnchor.
|
||||
type anchorInfo struct {
|
||||
schema *Schema
|
||||
dynamic bool
|
||||
}
|
||||
|
||||
// String returns a short description of the schema.
|
||||
func (s *Schema) String() string {
|
||||
if s.ID != "" {
|
||||
return s.ID
|
||||
if s.uri != nil {
|
||||
return s.uri.String()
|
||||
}
|
||||
if a := cmp.Or(s.Anchor, s.DynamicAnchor); a != "" {
|
||||
return fmt.Sprintf("%q, anchor %s", s.base.uri.String(), a)
|
||||
}
|
||||
// TODO: return something better, like a JSON Pointer from the base.
|
||||
return "<anonymous schema>"
|
||||
|
|
|
|||
Loading…
Reference in a new issue