This module provides JSON schema support. A JSON schema may be used to describe the structure and values that are permitted in a JSON object. These packages support creating JSON schemas by
- unmarshaling them from a JSON object
- building them piece by piece by calling functions
- inferring the JSON schema for a Go struct
Given a JSON schema, you can validate whether a Go struct or a JSON object satisfies the schema.
The implementation of JSON schemas is fairly efficient both in memory space and in determining whether an instance is valid according to a schema.
This module currently supports JSON schema versions draft-07, draft2019-09, and draft2020-12.
The module is flexible and support for more versions can be added.
It is possible to define your own custom JSON schema keywords, although this is not well tested. Custom vocabularies are not currently supported.
The current implementation and API should be considered experimental. While the basic approach and representation is unlikely to change, details may shift.
// VerifySchema takes a JSON encoded JSON schema and
// a set of encoded JSON values.
// If the returned error satisfies jsonschema.IsValidationError
// Then the error indicates that validating against the JSON schema failed.
// Otherwise there was an error in decoding or using the JSON values.
func VerifySchema(schemaBytes io.Reader, valsBytes []io.Reader) error {
var schema jsonschema.Schema
if err := json.NewDecoder(schemaBytes).Decode(&schema); err != nil {
return err
}
for _, valBytes := range valsBytes {
var val any
if err := json.NewDecoder(valBytes).Decode(&val); err != nil {
return err
}
if err := schema.Validate(val); err != nil {
return err
}
}
return nil
}// BuildPersonSchema builds a JSON schema
// that validates records with a name and an age.
// This uses the draft202012 JSON schema version;
// other schema versions would work as well.
func BuildPersonSchema() *jsonschema.Schema {
builder := draft202012.NewBuilder()
// Each JSON value must be an "object",
// meaning a map from string to value.
builder.AddType("object")
// Describe properties of the JSON object keys.
builder.AddProperties(map[string]*jsonschema.Schema{
// A name must be a string.
"name": draft202012.NewSubBuilder().
AddType("string").
Build(),
// An age must be an integer between 0 and 150.
"age": draft202012.NewSubBuilder().
AddType("integer").
AddMinimum(0).
AddMaximum(150).
Build(),
})
// The name and age properties must be present.
builder.AddRequired([]string{"name", "age"})
// No other properties may be present.
builder.AddAdditionalProperties(draft202012.NewSubBuilder.BoolSchema(false))
// Build and return the schema.
return builder.Build()
}For example, using this function like this:
var data = []map[string]any{
map[string]any{
"name": "me",
"age": 10,
},
map[string]any{
"age": 10,
},
map[string]any{
"name": "me",
"age": "me2",
},
map[string]any{
"name": "me",
"age": 10,
"citizenship": "USA",
},
}
func main() {
s := BuildPersonSchema()
for i, val := range data {
fmt.Printf("%d: %v\n", i, s.Validate(val))
}
}will print
0: <nil>
1: required/name: missing required field "name"
2: properties/age: instance has type "string", want "integer"
3: additionalProperties/citizenship: false schema never matches
// Person represent a person.
type Person struct {
Name string `json:"name"`
Age int `json:"age,omitempty"`
}
// PersonSchema returns a JSON schema for
// objects that will unmarshal into Person.
func PersonSchema() *jsonschema.Schema {
builder := draft202012.NewBuilder()
builder, err := draft202012.Infer[Person](builder, nil)
if err != nil {
// Everything here is completely under
// the program's control,
// so this should never fail.
log.Fatal(err)
}
// This will return the JSON schema
//
// {
// "$schema": "https://json-schema.org/draft/2020-12/schema",
// "type": "object"
// "properties": {
// "age": {
// "type": "integer"
// },
// "name": {
// "type": "string"
// }
// },
// "additionalProperties": false,
// "required": [
// "name"
// ],
// }
return builder.Build()
}The jsonschema package does not provide annotations to the calling code.
Validation errors are reported with a reference to the invalid location, but those references are not currently well defined JSON pointers.
The "pattern" and "patternProperties" keywords, and the "format" keyword with "regex", use regular expressions. The JSON schema specification calls for these regular expressions to use the ECMA 262 regular expression syntax. However, the jsonschema package uses the Go regexp package syntax instead.
The jsonschema package does not check the "format" keyword by default. Checking "format" requires both
- a blank import of "github.com/ianlancetaylor/jsonschema/format"
- calling the
Schema.ValidateWithOptsmethod with aValidateOptsvalue withValidateFormatset totrue
The jsonschema package does not check the "contentEncoding", "contendMediaType", or "contentSchema" keywords.