Documentation ¶
Overview ¶
Package jsonschema provides json-schema compilation and validation.
Features:
- implements draft 2020-12, 2019-09, draft-7, draft-6, draft-4
- fully compliant with JSON-Schema-Test-Suite, (excluding some optional)
- list of optional tests that are excluded can be found in schema_test.go(variable skipTests)
- validates schemas against meta-schema
- full support of remote references
- support of recursive references between schemas
- detects infinite loop in schemas
- thread safe validation
- rich, intuitive hierarchial error messages with json-pointers to exact location
- supports output formats flag, basic and detailed
- supports enabling format and content Assertions in draft2019-09 or above
- change Compiler.AssertFormat, Compiler.AssertContent to true
- compiled schema can be introspected. easier to develop tools like generating go structs given schema
- supports user-defined keywords via extensions
- implements following formats (supports user-defined)
- date-time, date, time, duration (supports leap-second)
- uuid, hostname, email
- ip-address, ipv4, ipv6
- uri, uriref, uri-template(limited validation)
- json-pointer, relative-json-pointer
- regex, format
- implements following contentEncoding (supports user-defined)
- base64
- implements following contentMediaType (supports user-defined)
- application/json
- can load from files/http/https/string/[]byte/io.Reader (supports user-defined)
The schema is compiled against the version specified in "$schema" property. If "$schema" property is missing, it uses latest draft which currently implemented by this library.
You can force to use specific draft, when "$schema" is missing, as follows:
compiler := jsonschema.NewCompiler() compiler.Draft = jsonschema.Draft4
This package supports loading json-schema from filePath and fileURL.
To load json-schema from HTTPURL, add following import:
import _ "github.com/santhosh-tekuri/jsonschema/v5/httploader"
you can validate yaml documents. see https://play.golang.org/p/sJy1qY7dXgA
Example ¶
sch, err := jsonschema.Compile("testdata/person_schema.json") if err != nil { log.Fatalf("%#v", err) } data, err := os.ReadFile("testdata/person.json") if err != nil { log.Fatal(err) } var v interface{} if err := json.Unmarshal(data, &v); err != nil { log.Fatal(err) } if err = sch.Validate(v); err != nil { log.Fatalf("%#v", err) }
Output:
Example (Extension) ¶
package main import ( "encoding/json" "fmt" "log" "strconv" "strings" "github.com/santhosh-tekuri/jsonschema/v5" ) var powerOfMeta = jsonschema.MustCompileString("powerOf.json", `{ "properties" : { "powerOf": { "type": "integer", "exclusiveMinimum": 0 } } }`) type powerOfCompiler struct{} func (powerOfCompiler) Compile(ctx jsonschema.CompilerContext, m map[string]interface{}) (jsonschema.ExtSchema, error) { if pow, ok := m["powerOf"]; ok { n, err := pow.(json.Number).Int64() return powerOfSchema(n), err } // nothing to compile, return nil return nil, nil } type powerOfSchema int64 func (s powerOfSchema) Validate(ctx jsonschema.ValidationContext, v interface{}) error { switch v.(type) { case json.Number, float32, float64, int, int8, int32, int64, uint, uint8, uint32, uint64: pow := int64(s) n, _ := strconv.ParseInt(fmt.Sprint(v), 10, 64) for n%pow == 0 { n = n / pow } if n != 1 { return ctx.Error("powerOf", "%v not powerOf %v", v, pow) } return nil default: return nil } } func main() { c := jsonschema.NewCompiler() c.RegisterExtension("powerOf", powerOfMeta, powerOfCompiler{}) schema := `{"powerOf": 10}` instance := `100` if err := c.AddResource("schema.json", strings.NewReader(schema)); err != nil { log.Fatal(err) } sch, err := c.Compile("schema.json") if err != nil { log.Fatalf("%#v", err) } var v interface{} if err := json.Unmarshal([]byte(instance), &v); err != nil { log.Fatal(err) } if err = sch.Validate(v); err != nil { log.Fatalf("%#v", err) } }
Output:
Example (FromString) ¶
Example_fromString shows how to load schema from string.
schema := `{"type": "object"}` instance := `{"foo": "bar"}` sch, err := jsonschema.CompileString("schema.json", schema) if err != nil { log.Fatalf("%#v", err) } var v interface{} if err := json.Unmarshal([]byte(instance), &v); err != nil { log.Fatal(err) } if err = sch.Validate(v); err != nil { log.Fatalf("%#v", err) }
Output:
Example (FromStrings) ¶
Example_fromStrings shows how to load schema from more than one string.
c := jsonschema.NewCompiler() if err := c.AddResource("main.json", strings.NewReader(`{"$ref":"obj.json"}`)); err != nil { log.Fatal(err) } if err := c.AddResource("obj.json", strings.NewReader(`{"type":"object"}`)); err != nil { log.Fatal(err) } sch, err := c.Compile("main.json") if err != nil { log.Fatalf("%#v", err) } var v interface{} if err := json.Unmarshal([]byte("{}"), &v); err != nil { log.Fatal(err) } if err = sch.Validate(v); err != nil { log.Fatalf("%#v", err) }
Output:
Example (UserDefinedContent) ¶
Example_userDefinedContent shows how to define "hex" contentEncoding and "application/xml" contentMediaType
c := jsonschema.NewCompiler() c.AssertContent = true c.Decoders["hex"] = hex.DecodeString c.MediaTypes["application/xml"] = func(b []byte) error { return xml.Unmarshal(b, new(interface{})) } schema := `{ "type": "object", "properties": { "xml" : { "type": "string", "contentEncoding": "hex", "contentMediaType": "application/xml" } } }` instance := `{"xml": "3c726f6f742f3e"}` if err := c.AddResource("schema.json", strings.NewReader(schema)); err != nil { log.Fatalf("%v", err) } sch, err := c.Compile("schema.json") if err != nil { log.Fatalf("%#v", err) } var v interface{} if err := json.Unmarshal([]byte(instance), &v); err != nil { log.Fatal(err) } if err = sch.Validate(v); err != nil { log.Fatalf("%#v", err) }
Output:
Example (UserDefinedFormat) ¶
Example_userDefinedFormat shows how to define 'odd-number' format.
c := jsonschema.NewCompiler() c.AssertFormat = true c.Formats["odd-number"] = func(v interface{}) bool { switch v := v.(type) { case json.Number, float32, float64, int, int8, int32, int64, uint, uint8, uint32, uint64: n, _ := strconv.ParseInt(fmt.Sprint(v), 10, 64) return n%2 != 0 default: return true } } schema := `{ "type": "integer", "format": "odd-number" }` instance := 5 if err := c.AddResource("schema.json", strings.NewReader(schema)); err != nil { log.Fatalf("%v", err) } sch, err := c.Compile("schema.json") if err != nil { log.Fatalf("%#v", err) } if err = sch.Validate(instance); err != nil { log.Fatalf("%#v", err) }
Output:
Example (UserDefinedLoader) ¶
Example_userDefinedLoader shows how to define custom schema loader.
we are implementing a "map" protocol which servers schemas from go map variable.
var schemas = map[string]string{ "main.json": `{"$ref":"obj.json"}`, "obj.json": `{"type":"object"}`, } jsonschema.Loaders["map"] = func(url string) (io.ReadCloser, error) { schema, ok := schemas[strings.TrimPrefix(url, "map:///")] if !ok { return nil, fmt.Errorf("%q not found", url) } return io.NopCloser(strings.NewReader(schema)), nil } sch, err := jsonschema.Compile("map:///main.json") if err != nil { log.Fatalf("%+v", err) } var v interface{} if err := json.Unmarshal([]byte("{}"), &v); err != nil { log.Fatal(err) } if err = sch.Validate(v); err != nil { log.Fatalf("%#v", err) }
Output:
Index ¶
- Variables
- type Basic
- type BasicError
- type Compiler
- func (c *Compiler) AddResource(url string, r io.Reader) error
- func (c *Compiler) AddResourceJSON(url string, doc interface{}) error
- func (c *Compiler) Compile(url string) (*Schema, error)
- func (c *Compiler) MustCompile(url string) *Schema
- func (c *Compiler) RegisterExtension(name string, meta *Schema, ext ExtCompiler)
- type CompilerContext
- type Detailed
- type Draft
- type ExtCompiler
- type ExtSchema
- type Flag
- type InfiniteLoopError
- type InvalidJSONTypeError
- type LoaderNotFoundError
- type Regexp
- type Schema
- type SchemaError
- type ValidationContext
- func (ctx ValidationContext) Error(keywordPath string, format string, a ...interface{}) *ValidationError
- func (ctx ValidationContext) EvaluatedItem(index int)
- func (ctx ValidationContext) EvaluatedProp(prop string)
- func (ctx ValidationContext) Validate(s *Schema, spath string, v interface{}, vpath string) error
- type ValidationError
- func (ve *ValidationError) BasicOutput() Basic
- func (ve *ValidationError) DetailedOutput() Detailed
- func (ve *ValidationError) Error() string
- func (ve *ValidationError) FlagOutput() Flag
- func (ve *ValidationError) GoString() string
- func (ValidationError) Group(parent *ValidationError, causes ...error) error
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( Draft4 = &Draft{version: 4, id: "id", boolSchema: false} Draft6 = &Draft{version: 6, id: "$id", boolSchema: true} Draft7 = &Draft{version: 7, id: "$id", boolSchema: true} Draft2019 = &Draft{ version: 2019, id: "$id", boolSchema: true, vocab: []string{ "https://json-schema.org/draft/2019-09/vocab/core", "https://json-schema.org/draft/2019-09/vocab/applicator", "https://json-schema.org/draft/2019-09/vocab/validation", "https://json-schema.org/draft/2019-09/vocab/meta-data", "https://json-schema.org/draft/2019-09/vocab/format", "https://json-schema.org/draft/2019-09/vocab/content", }, defaultVocab: []string{ "https://json-schema.org/draft/2019-09/vocab/core", "https://json-schema.org/draft/2019-09/vocab/applicator", "https://json-schema.org/draft/2019-09/vocab/validation", }, } Draft2020 = &Draft{ version: 2020, id: "$id", boolSchema: true, vocab: []string{ "https://json-schema.org/draft/2020-12/vocab/core", "https://json-schema.org/draft/2020-12/vocab/applicator", "https://json-schema.org/draft/2020-12/vocab/unevaluated", "https://json-schema.org/draft/2020-12/vocab/validation", "https://json-schema.org/draft/2020-12/vocab/meta-data", "https://json-schema.org/draft/2020-12/vocab/format-annotation", "https://json-schema.org/draft/2020-12/vocab/format-assertion", "https://json-schema.org/draft/2020-12/vocab/content", }, defaultVocab: []string{ "https://json-schema.org/draft/2020-12/vocab/core", "https://json-schema.org/draft/2020-12/vocab/applicator", "https://json-schema.org/draft/2020-12/vocab/unevaluated", "https://json-schema.org/draft/2020-12/vocab/validation", }, } )
supported drafts
var Decoders = map[string]func(string) ([]byte, error){ "base64": base64.StdEncoding.DecodeString, }
Decoders is a registry of functions, which know how to decode string encoded in specific format.
New Decoders can be registered by adding to this map. Key is encoding name, value is function that knows how to decode string in that format.
var Formats = map[string]func(interface{}) bool{
"date-time": isDateTime,
"date": isDate,
"time": isTime,
"duration": isDuration,
"period": isPeriod,
"hostname": isHostname,
"email": isEmail,
"ip-address": isIPV4,
"ipv4": isIPV4,
"ipv6": isIPV6,
"uri": isURI,
"iri": isURI,
"uri-reference": isURIReference,
"uriref": isURIReference,
"iri-reference": isURIReference,
"uri-template": isURITemplate,
"regex": isRegex,
"json-pointer": isJSONPointer,
"relative-json-pointer": isRelativeJSONPointer,
"uuid": isUUID,
}
Formats is a registry of functions, which know how to validate a specific format.
New Formats can be registered by adding to this map. Key is format name, value is function that knows how to validate that format.
var LoadURL = func(s string) (io.ReadCloser, error) { u, err := url.Parse(s) if err != nil { return nil, err } loader, ok := Loaders[u.Scheme] if !ok { return nil, LoaderNotFoundError(s) } return loader(s) }
LoadURL loads document at given absolute URL. The default implementation uses Loaders registry to lookup by schema and uses that loader.
Users can change this variable, if they would like to take complete responsibility of loading given URL. Used by Compiler if its LoadURL field is nil.
var Loaders = map[string]func(url string) (io.ReadCloser, error){
"file": loadFileURL,
}
Loaders is a registry of functions, which know how to load absolute url of specific schema.
New loaders can be registered by adding to this map. Key is schema, value is function that knows how to load url of that schema
var MediaTypes = map[string]func([]byte) error{
"application/json": validateJSON,
}
MediaTypes is a registry of functions, which know how to validate whether the bytes represent data of that mediaType.
New mediaTypes can be registered by adding to this map. Key is mediaType name, value is function that knows how to validate that mediaType.
Functions ¶
This section is empty.
Types ¶
type Basic ¶
type Basic struct { Valid bool `json:"valid"` Errors []BasicError `json:"errors"` }
Basic is output format with flat list of output units.
type BasicError ¶
type BasicError struct { KeywordLocation string `json:"keywordLocation"` AbsoluteKeywordLocation string `json:"absoluteKeywordLocation"` InstanceLocation string `json:"instanceLocation"` Error string `json:"error"` }
BasicError is output unit in basic format.
type Compiler ¶
type Compiler struct { // Draft represents the draft used when '$schema' attribute is missing. // // This defaults to latest supported draft (currently 2020-12). Draft *Draft // ExtractAnnotations tells whether schema annotations has to be extracted // in compiled Schema or not. ExtractAnnotations bool // LoadURL loads the document at given absolute URL. // // If nil, package global LoadURL is used. LoadURL func(s string) (io.ReadCloser, error) // CompileRegex comples given regular expression. // Defaults to golang's regexp implementation. // // NOTE: If you are overriding this, also ensure to override "regex" Format. CompileRegex func(s string) (Regexp, error) // Formats can be registered by adding to this map. Key is format name, // value is function that knows how to validate that format. Formats map[string]func(interface{}) bool // AssertFormat for specifications >= draft2019-09. AssertFormat bool // Decoders can be registered by adding to this map. Key is encoding name, // value is function that knows how to decode string in that format. Decoders map[string]func(string) ([]byte, error) // MediaTypes can be registered by adding to this map. Key is mediaType name, // value is function that knows how to validate that mediaType. MediaTypes map[string]func([]byte) error // AssertContent for specifications >= draft2019-09. AssertContent bool // contains filtered or unexported fields }
A Compiler represents a json-schema compiler.
func NewCompiler ¶
func NewCompiler() *Compiler
NewCompiler returns a json-schema Compiler object. if '$schema' attribute is missing, it is treated as draft7. to change this behavior change Compiler.Draft value
func (*Compiler) AddResource ¶
AddResource adds in-memory resource to the compiler.
Note that url must not have fragment
func (*Compiler) AddResourceJSON ¶
AddResourceJSON adds in-memory resource from given json value.
func (*Compiler) Compile ¶
Compile parses json-schema at given url returns, if successful, a Schema object that can be used to match against json.
error returned will be of type *SchemaError
func (*Compiler) MustCompile ¶
MustCompile is like Compile but panics if the url cannot be compiled to *Schema. It simplifies safe initialization of global variables holding compiled Schemas.
func (*Compiler) RegisterExtension ¶
func (c *Compiler) RegisterExtension(name string, meta *Schema, ext ExtCompiler)
RegisterExtension registers custom keyword(s) into this compiler.
name is extension name, used only to avoid name collisions. meta captures the metaschema for the new keywords. This is used to validate the schema before calling ext.Compile.
type CompilerContext ¶
type CompilerContext struct {
// contains filtered or unexported fields
}
CompilerContext provides additional context required in compiling for extension.
func (CompilerContext) Compile ¶
func (ctx CompilerContext) Compile(schPath string, applicableOnSameInstance bool) (*Schema, error)
Compile compiles given value at ptr into *Schema. This is useful in implementing keyword like allOf/not/patternProperties.
schPath is the relative-json-pointer to the schema to be compiled from parent schema.
applicableOnSameInstance tells whether current schema and the given schema are applied on same instance value. this is used to detect infinite loop in schema.
func (CompilerContext) CompileRef ¶
func (ctx CompilerContext) CompileRef(ref string, refPath string, applicableOnSameInstance bool) (*Schema, error)
CompileRef compiles the schema referenced by ref uri
refPath is the relative-json-pointer to ref.
applicableOnSameInstance tells whether current schema and the given schema are applied on same instance value. this is used to detect infinite loop in schema.
type Detailed ¶
type Detailed struct { Valid bool `json:"valid"` KeywordLocation string `json:"keywordLocation"` AbsoluteKeywordLocation string `json:"absoluteKeywordLocation"` InstanceLocation string `json:"instanceLocation"` Error string `json:"error,omitempty"` Errors []Detailed `json:"errors,omitempty"` }
Detailed is output format based on structure of schema.
type Draft ¶
type Draft struct {
// contains filtered or unexported fields
}
A Draft represents json-schema draft
type ExtCompiler ¶
type ExtCompiler interface { // Compile compiles the custom keywords in schema m and returns its compiled representation. // if the schema m does not contain the keywords defined by this extension, // compiled representation nil should be returned. Compile(ctx CompilerContext, m map[string]interface{}) (ExtSchema, error) }
ExtCompiler compiles custom keyword(s) into ExtSchema.
type ExtSchema ¶
type ExtSchema interface { // Validate validates the json value v with this ExtSchema. // Returned error must be *ValidationError. Validate(ctx ValidationContext, v interface{}) error }
ExtSchema is schema representation of custom keyword(s)
type Flag ¶
type Flag struct {
Valid bool `json:"valid"`
}
Flag is output format with simple boolean property valid.
type InfiniteLoopError ¶
type InfiniteLoopError string
InfiniteLoopError is returned by Compile/Validate. this gives url#keywordLocation that lead to infinity loop.
func (InfiniteLoopError) Error ¶
func (e InfiniteLoopError) Error() string
type InvalidJSONTypeError ¶
type InvalidJSONTypeError string
InvalidJSONTypeError is the error type returned by ValidateInterface. this tells that specified go object is not valid jsonType.
func (InvalidJSONTypeError) Error ¶
func (e InvalidJSONTypeError) Error() string
type LoaderNotFoundError ¶
type LoaderNotFoundError string
LoaderNotFoundError is the error type returned by Load function. It tells that no Loader is registered for that URL Scheme.
func (LoaderNotFoundError) Error ¶
func (e LoaderNotFoundError) Error() string
type Regexp ¶
type Regexp interface { // MatchString reports whether the string s contains any match of the regular expression. MatchString(s string) bool // String returns the source text used to compile the regular expression. String() string }
Regexp is the representation of a compiled regular expression. A Regexp is safe for concurrent use by multiple goroutines.
type Schema ¶
type Schema struct { Location string // absolute location Draft *Draft // draft used by schema. // type agnostic validations Format string Always *bool // always pass/fail. used when booleans are used as schemas in draft-07. Ref *Schema RecursiveAnchor bool RecursiveRef *Schema DynamicAnchor string DynamicRef *Schema Types []string // allowed types. Constant []interface{} // first element in slice is constant value. note: slice is used to capture nil constant. Enum []interface{} // allowed values. Not *Schema AllOf []*Schema AnyOf []*Schema OneOf []*Schema If *Schema Then *Schema // nil, when If is nil. Else *Schema // nil, when If is nil. // object validations MinProperties int // -1 if not specified. MaxProperties int // -1 if not specified. Required []string // list of required properties. Properties map[string]*Schema PropertyNames *Schema RegexProperties bool // property names must be valid regex. used only in draft4 as workaround in metaschema. PatternProperties map[Regexp]*Schema AdditionalProperties interface{} // nil or bool or *Schema. Dependencies map[string]interface{} // map value is *Schema or []string. DependentRequired map[string][]string DependentSchemas map[string]*Schema UnevaluatedProperties *Schema // array validations MinItems int // -1 if not specified. MaxItems int // -1 if not specified. UniqueItems bool Items interface{} // nil or *Schema or []*Schema AdditionalItems interface{} // nil or bool or *Schema. PrefixItems []*Schema Items2020 *Schema // items keyword reintroduced in draft 2020-12 Contains *Schema ContainsEval bool // whether any item in an array that passes validation of the contains schema is considered "evaluated" MinContains int // 1 if not specified MaxContains int // -1 if not specified UnevaluatedItems *Schema // string validations MinLength int // -1 if not specified. MaxLength int // -1 if not specified. Pattern Regexp ContentEncoding string ContentMediaType string ContentSchema *Schema // number validators Minimum *big.Rat ExclusiveMinimum *big.Rat Maximum *big.Rat ExclusiveMaximum *big.Rat MultipleOf *big.Rat // annotations. captured only when Compiler.ExtractAnnotations is true. Title string Description string Default interface{} Comment string ReadOnly bool WriteOnly bool Examples []interface{} Deprecated bool // user defined extensions Extensions map[string]ExtSchema // contains filtered or unexported fields }
A Schema represents compiled version of json-schema.
func Compile ¶
Compile parses json-schema at given url returns, if successful, a Schema object that can be used to match against json.
Returned error can be *SchemaError
func CompileString ¶
CompileString parses and compiles the given schema with given base url.
func MustCompile ¶
MustCompile is like Compile but panics if the url cannot be compiled to *Schema. It simplifies safe initialization of global variables holding compiled Schemas.
func MustCompileString ¶
MustCompileString is like CompileString but panics on error. It simplified safe initialization of global variables holding compiled Schema.
func (*Schema) Validate ¶
Validate validates given doc, against the json-schema s.
the v must be the raw json value. for number precision unmarshal with json.UseNumber().
returns *ValidationError if v does not confirm with schema s. returns InfiniteLoopError if it detects loop during validation. returns InvalidJSONTypeError if it detects any non json value in v.
type SchemaError ¶
type SchemaError struct { // SchemaURL is the url to json-schema that filed to compile. // This is helpful, if your schema refers to external schemas SchemaURL string // Err is the error that occurred during compilation. // It could be ValidationError, because compilation validates // given schema against the json meta-schema Err error }
SchemaError is the error type returned by Compile.
func (*SchemaError) Error ¶
func (se *SchemaError) Error() string
func (*SchemaError) GoString ¶
func (se *SchemaError) GoString() string
func (*SchemaError) Unwrap ¶
func (se *SchemaError) Unwrap() error
type ValidationContext ¶
type ValidationContext struct {
// contains filtered or unexported fields
}
ValidationContext provides additional context required in validating for extension.
func (ValidationContext) Error ¶
func (ctx ValidationContext) Error(keywordPath string, format string, a ...interface{}) *ValidationError
Error used to construct validation error by extensions.
keywordPath is relative-json-pointer to keyword.
func (ValidationContext) EvaluatedItem ¶
func (ctx ValidationContext) EvaluatedItem(index int)
EvaluatedItem marks given index of array as evaluated.
func (ValidationContext) EvaluatedProp ¶
func (ctx ValidationContext) EvaluatedProp(prop string)
EvaluatedProp marks given property of object as evaluated.
func (ValidationContext) Validate ¶
func (ctx ValidationContext) Validate(s *Schema, spath string, v interface{}, vpath string) error
Validate validates schema s with value v. Extension must use this method instead of *Schema.ValidateInterface method. This will be useful in implementing keywords like allOf/oneOf
spath is relative-json-pointer to s vpath is relative-json-pointer to v.
type ValidationError ¶
type ValidationError struct { KeywordLocation string // validation path of validating keyword or schema AbsoluteKeywordLocation string // absolute location of validating keyword or schema InstanceLocation string // location of the json value within the instance being validated Message string // describes error Causes []*ValidationError // nested validation errors }
ValidationError is the error type returned by Validate.
func (*ValidationError) BasicOutput ¶
func (ve *ValidationError) BasicOutput() Basic
BasicOutput returns output in basic format
func (*ValidationError) DetailedOutput ¶
func (ve *ValidationError) DetailedOutput() Detailed
DetailedOutput returns output in detailed format
func (*ValidationError) Error ¶
func (ve *ValidationError) Error() string
func (*ValidationError) FlagOutput ¶
func (ve *ValidationError) FlagOutput() Flag
FlagOutput returns output in flag format
func (*ValidationError) GoString ¶
func (ve *ValidationError) GoString() string
func (ValidationError) Group ¶
func (ValidationError) Group(parent *ValidationError, causes ...error) error
Group is used by extensions to group multiple errors as causes to parent error. This is useful in implementing keywords like allOf where each schema specified in allOf can result a validationError.
Source Files ¶
Directories ¶
Path | Synopsis |
---|---|
Package httploader implements loader.Loader for http/https url.
|
Package httploader implements loader.Loader for http/https url. |