jsonschema

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 11, 2026 License: Apache-2.0 Imports: 19 Imported by: 0

README

jsonschema

Validation against families of JSON Schema documents, with every failure as a JSON pointer, a keyword and a short message, in a stable order. A layer over santhosh-tekuri/jsonschema/v6.

Go Reference test Go

Published schema families, such as the Open Charge Alliance's OCPP and OSCP schemas, are sets of files that refer to each other by file name, $id or JSON pointer, and sometimes name a file differently from its $id. This module keeps such a set in memory as added (corrections can be applied to a decoded copy first), resolves the references, validates with santhosh-tekuri/jsonschema/v6, and reports each failure in a form a protocol reply can carry. A pre-pass (CheckSchemas) refuses schemas that use keywords outside a dialect; the OCA's OCPP dialect is included. No cgo. Written for SigmaM2M, a oneM2M platform, where it validates OCPP 1.6 to 2.1 and OSCP 2.0 messages and its OpenAPI requests.

Features

Feature Status
Drafts 4, 6, 7, 2019-09 and 2020-12, chosen by each document's $schema (draft-07 when it names none) yes (the library's)
Schema families: references by $id, by a document added under the whole URL, then by file name; nothing fetched from the network yes
Failures as pointer, keyword and our message, in a stable order (ValidationError, Translate, Style) yes
Numbers kept exact (Decode with json.Number; integer accepts any number whose fraction is zero) yes
Formats checked with our messages: date-time, date, time (with ranges and leap seconds), email, uri, uri-reference, ipv4, ipv6, uuid yes
Formats hostname, duration, json-pointer, relative-json-pointer, iri, iri-reference, uri-template not checked (accepted)
Formats idn-hostname, idn-email not checked (the library has no checker)
format as an annotation only, as 2020-12 defaults to no: always asserted
Dialects: refuse keywords outside an allowed set before use (Dialect, CheckSchemas, OCA()) yes
pattern with ECMA-262 semantics no: Go RE2 syntax (the library's)
Output formats of the specification (flag, basic, detailed) no: use the library directly

Choosing a Go JSON Schema validator

Compared from each project's source or README at the versions named, in October 2026. Check the projects themselves before relying on a row.

this module santhosh-tekuri/jsonschema v6.0.3 kaptinlin/jsonschema v0.9.9 xeipuuv/gojsonschema v1.2.0
What it is a layer over santhosh-tekuri v6 a validator a validator a validator
Drafts 4, 6, 7, 2019-09, 2020-12 4, 6, 7, 2019-09, 2020-12 4, 6, 7, 2019-09, 2020-12 4, 6, 7
Default draft without $schema 7 2020-12 2020-12 not compared
JSON Schema Test Suite run here (below) run in its CI not stated in its README not compared
Families with mismatched file names and $id yes through a custom loader not compared not compared
Failure list in a stable order, own wording yes output formats of the specification its own its own
Keyword dialect refusal before use yes vocabularies (2019-09 and later) not compared no
Last release v0.x 2026-08 2026-08 2019-10

Pick this module when you validate protocol messages against a family of published schemas and need failures you can put in a reply, in the same order every time. Pick santhosh-tekuri/jsonschema/v6 directly for everything else: it is the engine here, and gives the specification's output formats. Pick kaptinlin/jsonschema to validate Go structs or to build schemas in code.

Install

go get github.com/cB-Sigma/jsonschema@latest

Requires Go 1.26 or later.

Quick start

import "github.com/cB-Sigma/jsonschema"

set := jsonschema.NewSet()
err := set.Add("meter.json", meterSchema)    // refers to "reading.json"
err = set.Add("reading.json", readingSchema)

err = set.ValidateJSON("meter.json", body)
var v jsonschema.ValidationError
if errors.As(err, &v) {
	for _, e := range v {
		fmt.Println(e.Path, e.Keyword, e.Message) // "/reading/kWh minimum ..."
	}
}

Example in example_test.go runs this and checks its output.

Documentation

API reference: https://pkg.go.dev/github.com/cB-Sigma/jsonschema

Package What it does
jsonschema (module root) NewSet, NewDialectSet, Set.Add, AddValue, Compile, ValidateJSON, Validate, Decode; ValidationError, Translate and Style (the wording of the library's failures, for callers that compile with the library themselves); Dialect, CheckSchemas, OCA()
testsuite (nested module, tests only) runs the JSON Schema Test Suite against this module

Testing

go test -race ./...

Tests carry // Covers: SIG-... comments: these are requirement IDs of the SigmaM2M project's compliance report and have no meaning outside it.

JSON Schema Test Suite

The JSON-Schema-Test-Suite (MIT) is vendored in the test-only nested module testsuite/, at commit 7de0e6a06031ede80028583dd45d0cd41105ae5b (2026-10-09). Its test runs every case of draft4, draft6, draft7, draft2019-09 and draft2020-12 through NewSet, Set.Add, Set.Compile, Decode and Set.Validate, with the suite's remotes added to each set under their http://localhost:1234/ URLs. A schema without $schema is given the directory's draft before Add, which is how a caller picks a draft other than the draft-07 default.

Draft Required Optional Optional, format
draft4 618/618 85/100 253/291
draft6 841/841 92/106 336/414
draft7 929/929 100/118 607/804
draft2019-09 1261/1261 144/158 663/885
draft2020-12 1292/1301 148/162 663/885

Run it with cd testsuite && go test -v ./.... The test fails when a test outside testsuite/testdata/known-failures.txt fails, when a listed test passes, or when a listed test is no longer in the suite. The 841 listed failures, by cause:

Cause Tests What it is
format not checked 408 the formats listed as not checked above
own format checker 133 email follows Go's net/mail (no IPv6 literals or quoted local parts with spaces; accepts some invalid domains and non-ASCII); uri and uri-reference follow Go's net/url and accept characters the suite refuses
format unknown 189 idn-hostname and idn-email
RE2 patterns 97 pattern, patternProperties and format regex use Go's RE2 syntax, not ECMA-262
format asserted 9 draft2020-12's required format.json expects format to be an annotation
content not asserted 4 draft7 optional: contentEncoding and contentMediaType carry no check
integer with zero fraction 1 draft4 optional: 1.0 counts as an integer in every draft

Every required test passes except the nine on format as an annotation. The run found and fixed two bugs in this module: a document added under a full URL without an $id (as the suite's remotes are) was not found by that URL, and the time and date-time checks accepted hours, minutes and offsets out of range and refused leap seconds.

Interop tested against
  • The JSON Schema Test Suite, above.
  • The OCA OSCP 2.0 schemas and OCPP 1.6, 2.0.1 and 2.1 schemas, through SigmaM2M's tests. On 2026-10-06 SigmaM2M's earlier hand-written validator and this module gave identical failure lists, in order, for 146,000 generated OCPP messages (68,113 of them failing) across all OCA schemas.

None. This module has no simulator.

Status

v0.x: the API may change before v1.

Contributing

See CONTRIBUTING.md.

Security

See SECURITY.md to report a vulnerability privately.

Licence

Apache-2.0 (see LICENSE and NOTICE). Copyright 2026 Abhinav Gautam.

Documentation

Overview

Package jsonschema validates JSON documents against families of JSON Schema documents (the Open Charge Alliance's OCPP and OSCP schemas) with github.com/santhosh-tekuri/jsonschema/v6, and reports each failure as a JSON pointer, the schema keyword and a short message of our own.

A Set holds a family of schema documents that refer to each other by relative file name ("phase-indicator.json") or JSON pointer ("#/definitions/X", "other.json#/definitions/Y"). References are resolved against the referring document's $id; when no document carries the resolved $id, the document added under that whole URL is used, then the one added under the same file name (some published sets name a file differently from its $id). Each document is validated by the draft its $schema names (draft-04 to 2020-12; draft-07 when it names none).

What this package adds to the library: the documents are kept in memory as added (corrections can be applied to a decoded copy before Add), the formats date-time, date, time, email, uri, uri-reference, ipv4, ipv6 and uuid are checked with our messages and the library's other formats are not checked, failures are listed in a stable order (see Translate), and CheckSchemas walks the schemas before use to refuse keywords outside a dialect (dialect.go).

Instances are decoded with json.Number (Decode), so integers keep their precision and "integer" accepts any number whose fraction is zero.

Example

A family of two schema documents that refer to each other by file name; every failure comes back as pointer, keyword and message, in order.

package main

import (
	"errors"
	"fmt"

	"github.com/cB-Sigma/jsonschema"
)

func main() {
	set := jsonschema.NewSet()
	_ = set.Add("meter.json", []byte(`{
		"$schema": "http://json-schema.org/draft-07/schema#",
		"type": "object",
		"properties": {
			"id": {"type": "string", "minLength": 1},
			"reading": {"$ref": "reading.json"}
		},
		"required": ["id", "reading"]
	}`))
	_ = set.Add("reading.json", []byte(`{
		"type": "object",
		"properties": {"kWh": {"type": "number", "minimum": 0}, "at": {"type": "string", "format": "date-time"}},
		"required": ["kWh"]
	}`))

	fmt.Println(set.ValidateJSON("meter.json", []byte(`{"id": "m1", "reading": {"kWh": 12.5}}`)))

	err := set.ValidateJSON("meter.json", []byte(`{"id": "", "reading": {"kWh": -1, "at": "yesterday"}}`))
	var v jsonschema.ValidationError
	if errors.As(err, &v) {
		for _, e := range v {
			fmt.Println(e.Path, e.Keyword)
		}
	}
}
Output:
<nil>
/id minLength
/reading/at format
/reading/kWh minimum

Index

Examples

Constants

This section is empty.

Variables

View Source
var Keywords = map[string]bool{
	"type": true, "enum": true, "const": true, "properties": true, "required": true, "additionalProperties": true,
	"patternProperties": true, "propertyNames": true, "minProperties": true, "maxProperties": true, "dependencies": true,
	"items": true, "additionalItems": true, "contains": true, "minItems": true, "maxItems": true, "uniqueItems": true,
	"minimum": true, "maximum": true, "exclusiveMinimum": true, "exclusiveMaximum": true, "multipleOf": true,
	"minLength": true, "maxLength": true, "pattern": true, "format": true, "allOf": true, "anyOf": true, "oneOf": true,
	"not": true, "if": true, "then": true, "else": true, "$ref": true,
	"$schema": true, "$id": true, "$comment": true, "title": true, "description": true, "default": true, "examples": true,
	"definitions": true, "readOnly": true, "writeOnly": true, "contentMediaType": true, "contentEncoding": true,
}

Keywords are the draft-07 keywords (and $schema, $id, $comment, title, description, default, examples, definitions, readOnly, writeOnly and contentMediaType/contentEncoding, which carry no check) that a dialect accepts by default in CheckSchemas.

Functions

func Decode

func Decode(data []byte) (any, error)

Decode parses JSON the way Validate expects instances (numbers as json.Number); trailing data is an error.

Types

type CheckContext

type CheckContext struct {
	// contains filtered or unexported fields
}

CheckContext is what a keyword's Check sees while CheckSchemas walks.

func (*CheckContext) Fail

func (c *CheckContext) Fail(format string, a ...any)

Fail reports a problem with the keyword's value.

func (*CheckContext) FailAt

func (c *CheckContext) FailAt(pointer, format string, a ...any)

FailAt reports a problem at another pointer of the document.

func (*CheckContext) Pointer

func (c *CheckContext) Pointer() string

Pointer is the JSON pointer of the keyword in its document.

type Dialect

type Dialect struct {
	// Keywords adds keywords to those CheckSchemas accepts, with a check
	// of their value.
	Keywords map[string]Keyword

	// Allowed says which keywords CheckSchemas accepts beside those in
	// Keywords; nil accepts the package's Keywords list.
	Allowed func(keyword string) bool

	// ObjectSchemasOnly makes CheckSchemas refuse boolean schemas (other
	// than additionalProperties: false/true, which is a keyword form).
	ObjectSchemasOnly bool
}

A Dialect is what CheckSchemas accepts in a family of schemas: the keywords allowed, and checks of a keyword's value. Validation itself is the library's (see jsonschema.go); a dialect only refuses, before use, schemas the validator would not check as their authors meant. Every field is optional.

func OCA

func OCA() *Dialect

OCA is the dialect of the Open Charge Alliance's OCPP schemas: OCPP 1.6 (draft-04), OCPP 2.0.1 and 2.1 (draft-06). What they use is a subset of draft-07 with the same meaning, plus annotations: "id" (the draft-04 document identifier), "javaType" and "comment". Any other keyword is refused by CheckSchemas, so a later schema that relies on a keyword this package does not implement cannot be checked only halfway. The draft-04 boolean form of exclusiveMinimum/exclusiveMaximum means something else in draft-07 and is refused too.

type Error

type Error struct {
	// Path is the JSON pointer of the failing value in the instance.
	Path string
	// Keyword is the schema keyword that failed ("false" for a false
	// schema, "json" for a document that is not JSON).
	Keyword string
	Message string
}

Error is one failed check.

func (Error) String

func (e Error) String() string

type Keyword

type Keyword struct {
	// Check runs when CheckSchemas meets the keyword in a schema, to
	// report a value of the wrong shape.
	Check func(c *CheckContext, schema map[string]any, value any)
}

Keyword is the dialect's treatment of one keyword.

type Set

type Set struct {
	// contains filtered or unexported fields
}

Set is a family of schema documents. It is safe for concurrent use once every document is added.

func NewDialectSet

func NewDialectSet(d *Dialect) *Set

NewDialectSet returns an empty set whose CheckSchemas applies the dialect.

func NewSet

func NewSet() *Set

NewSet returns an empty set.

func (*Set) Add

func (s *Set) Add(name string, raw []byte) error

Add parses a schema document and adds it under name (its file name).

func (*Set) AddValue

func (s *Set) AddValue(name string, root any) error

AddValue adds a schema document that is already decoded (map[string]any, []any, string, bool, nil and json.Number values), for instance one read from YAML.

func (*Set) CheckSchemas

func (s *Set) CheckSchemas(name string, pointers ...string) ([]string, error)

CheckSchemas walks the schemas at the given JSON pointers of document name, and every schema they contain or reference, before any validation: keywords the dialect does not allow are refused, each keyword's Check runs, references must resolve, and a schema must not reach itself through $ref, allOf, anyOf, oneOf, not, if, then or else alone (validating it would never reach a value). Errors carry pointers into the documents. It returns the pointers of the schemas it visited in document name.

func (*Set) Compile

func (s *Set) Compile(name string) (*v6.Schema, error)

Compile compiles the named document (and what it references) once, so that a schema the library refuses is reported before any validation. The first call freezes the set.

func (*Set) Names

func (s *Set) Names() []string

Names lists the documents in the set.

func (*Set) Validate

func (s *Set) Validate(name string, instance any) error

Validate checks an instance decoded with Decode (or any value built of map[string]any, []any, string, bool, nil, json.Number and float64). A failed check is a ValidationError; a schema that cannot be compiled is another error.

func (*Set) ValidateJSON

func (s *Set) ValidateJSON(name string, data []byte) error

ValidateJSON decodes data and validates it against the named document.

type Style

type Style struct {
	// Bound prints the limit of minimum, maximum and the exclusive bounds
	// (default: eight decimals); Multiple prints multipleOf's divisor
	// (default: a fraction such as 1/100).
	Bound, Multiple func(*big.Rat) string
	// Pattern starts the message of a failed pattern (default "does not
	// match "); the pattern follows.
	Pattern string
	// ClosestBranch adds, after an anyOf or oneOf that no branch passes,
	// the failures of the branch with the fewest, each message starting
	// "(closest branch) ".
	ClosestBranch bool
	// Max caps the failures listed (0: no cap).
	Max int
	// Last lists keywords whose failures at a location come after every
	// other failure there and below it, in this order.
	Last []string
}

Style is how Translate words its messages. The zero value is the wording of a Set.

type ValidationError

type ValidationError []Error

ValidationError lists every failed check of one validation.

func Translate

func Translate(err error, instance any, st *Style, schemaAt func(loc string) map[string]any) ValidationError

Translate turns a validation failure of github.com/santhosh-tekuri/jsonschema/v6 into a ValidationError: one Error per failed check, with our messages, listed depth first by instance location (members by name, items by index), and at one location in keyword order: type, enum, const, the object, array, string and number keywords, then dependencies, contains, anyOf, oneOf and not. Failures under allOf, $ref, if/then/else and propertyNames are listed where they occur; anyOf and oneOf report one failure each (with the closest branch if the style asks). schemaAt, when not nil, finds a schema object by the location the library reports, to name multiple types in the schema's order. Any other error is returned as nil.

func (ValidationError) Error

func (v ValidationError) Error() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL