jsonschema

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: May 4, 2026 License: MIT Imports: 39 Imported by: 0

README

go-rotini/jsonschema

A Go JSON Schema package that compiles, validates, and generates schemas, with multi-format input (JSON, JSONC, YAML, TOML).

This package is used as the default JSON Schema support package for rotini.

Features

  • Full Draft 2020-12 support, plus read-only support for Draft 2019-09, Draft 7, Draft 6, and Draft 4
  • Compile/validate split (Compile*SchemaValidate) so compilation amortizes across many validations
  • All four output formats from Draft 2020-12 §12: Flag, Basic, Detailed, Verbose
  • Generic ValidateTo[T] typed-decode in one call
  • Schema generation from Go types via reflection (Generate, GenerateBytes, FromType)
  • Multi-format instance and schema input (JSONC, YAML, TOML) via LoadJSONC / LoadYAML / LoadTOML and their Validate* counterparts
  • $ref, $dynamicRef, $recursiveRef, plain-name anchors, and pluggable Loader (HTTPS-only by default; opt-in HTTP / file)
  • Built-in format validators: date-time, date, time, duration, email, idn-email, hostname, idn-hostname, ipv4, ipv6, uri, uri-reference, iri, iri-reference, uri-template, json-pointer, relative-json-pointer, uuid, regex
  • Content vocabulary: contentEncoding, contentMediaType, contentSchema (annotation-only by default)
  • Two strict modes: WithMetaSchemaValidation (compile-time meta-schema check) and WithFormatAssertion (runtime format assertion)
  • OpenAPI 3.1 dialect support (VocabOAS, OASDialectURL)
  • Bowtie connector for cross-implementation conformance testing
  • RenderError for human-readable validation errors with a source pointer into the instance
  • DoS protection: max ref depth, max recursion depth, max document size, ref-loop detection

Installation

go get github.com/go-rotini/jsonschema

Requires Go 1.26 or later.

Quick Start

package main

import (
	"fmt"
	"log"

	"github.com/go-rotini/jsonschema"
)

type User struct {
	Name  string `json:"name"  jsonschema:"required,minLength=1"`
	Email string `json:"email" jsonschema:"required,format=email"`
	Age   int    `json:"age,omitempty" jsonschema:"minimum=0,maximum=150"`
}

func main() {
	// Compile a schema once; validate many instances against it.
	schemaJSON := []byte(`{
		"$schema": "https://json-schema.org/draft/2020-12/schema",
		"type": "object",
		"properties": {
			"name":  {"type": "string", "minLength": 1},
			"email": {"type": "string", "format": "email"}
		},
		"required": ["name", "email"]
	}`)

	schema, err := jsonschema.Compile(schemaJSON)
	if err != nil {
		log.Fatal(err)
	}

	// Validate an instance.
	instance := []byte(`{"name": "Ada", "email": "ada@example.com"}`)
	result, err := schema.Validate(instance)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("valid:", result.Valid)

	// Validate then decode into a typed value in one call.
	user, err := jsonschema.ValidateTo[User](schema, instance)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%+v\n", user)

	// Generate a schema from a Go type.
	generated, err := jsonschema.GenerateBytes(User{})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(string(generated))
}

Schema Generation From Go Types

Two tags drive generation:

  • json — standard library semantics: property name, omitempty, -.
  • jsonschema — schema-specific options.

Tag option vocabulary:

Option JSON Schema keyword
required adds field to parent required
description=... description
title=... title
default=... default
examples=v1|v2 examples
deprecated, readOnly, writeOnly flag
enum=a|b|c enum
const=v const
format=name format
minimum=N, maximum=N, exclusiveMinimum=N, exclusiveMaximum=N, multipleOf=N numeric
minLength=N, maxLength=N, pattern=re string
minItems=N, maxItems=N, uniqueItems array
minProperties=N, maxProperties=N, additionalProperties=false object
$id=uri, $ref=uri identity / reference

Self-referential types are emitted as $defs entries with internal $ref links.

type Tree struct {
	Label    string  `json:"label"`
	Children []*Tree `json:"children,omitempty"`
}

schema, _ := jsonschema.GenerateBytes(Tree{})
fmt.Println(string(schema))
// {"$schema":"https://json-schema.org/draft/2020-12/schema",
//  "$ref":"#/$defs/Tree",
//  "$defs":{"Tree":{"type":"object","properties":{
//    "label":{"type":"string"},
//    "children":{"type":"array","items":{"$ref":"#/$defs/Tree"}}}}}}

Multi-Format Input

Schemas and instances can be authored as JSONC, YAML, or TOML. The adapters delegate to the corresponding rotini packages and preserve numeric literals via json.Number so multipleOf / minimum / maximum evaluate against the wire form.

schemaYAML := []byte(`
type: object
properties:
  port:
    type: integer
    minimum: 1
    maximum: 65535
required: [port]
`)

schema, err := jsonschema.LoadYAML(schemaYAML)
if err != nil {
	log.Fatal(err)
}

instanceTOML := []byte(`port = 8080`)
result, err := jsonschema.ValidateTOML(schema, instanceTOML)
if err != nil {
	log.Fatal(err)
}
fmt.Println("valid:", result.Valid)

Equivalent entry points exist for JSONC: LoadJSONC and ValidateJSONC.

Output Formats

Result.Output renders a validation result in any of the four formats from Draft 2020-12 §12 — Flag, Basic, Detailed, Verbose.

schema := jsonschema.MustCompile([]byte(`{
	"type": "object",
	"properties": {"name": {"type": "string", "minLength": 3}},
	"required": ["name"]
}`))

result, _ := schema.Validate([]byte(`{"name": "x"}`))

fmt.Println(string(result.Output(jsonschema.OutputFlag)))
// {"valid":false}

fmt.Println(string(result.Output(jsonschema.OutputBasic)))
// {"valid":false,"keywordLocation":"","instanceLocation":"","errors":[
//   {"valid":false,"keywordLocation":"","instanceLocation":"","error":"validation failed"},
//   {"valid":false,"keywordLocation":"/properties/name/minLength",
//    "instanceLocation":"/name","error":"string length 1 is below minLength 3"}
// ]}

OutputDetailed emits a nested tree pruned to failing branches; OutputVerbose keeps the full tree including passing groups. The wire shape validates against the spec's output meta-schema (also exposed via OutputMetaSchema).

Integrating With encoding/json

The standard library does not have a schema layer, so there is no drop-in replacement to mirror. Instead, the two libraries integrate at the boundary: Schema.ValidateAndUnmarshal validates instanceJSON against the schema and, on success, decodes it via encoding/json.Unmarshal in a single call.

var u User
if err := schema.ValidateAndUnmarshal(instance, &u); err != nil {
	// err is either a *jsonschema.ValidationError chain or a json.Unmarshal error.
	log.Fatal(err)
}

Schema.MarshalJSON returns canonical schema bytes that round-trip through encoding/json.Unmarshal + CompileValue.

Documentation

Full API reference is available on pkg.go.dev.

Contributing

See CONTRIBUTING.md for guidelines on how to contribute to this project.

Code of Conduct

This project follows a code of conduct to ensure a welcoming community. See CODE_OF_CONDUCT.md.

Security

To report a vulnerability, see SECURITY.md.

License

This project is licensed under the MIT License. See LICENSE for details.

Documentation

Overview

Package jsonschema implements JSON Schema validation, schema generation from Go types, and multi-format input via the rotini package family.

The package follows the conventions of encoding/json: a small set of package-level entry points (Compile, Validate, Generate) for one-shot use, and a compiled *Schema value for amortizing compilation across many validations. Functional options shape compile-time and run-time behavior.

schema, err := jsonschema.Compile(schemaJSON)
if err != nil { return err }
result, err := schema.Validate(instanceJSON)
if err != nil { return err }
if !result.Valid { /* inspect result.Errors */ }

Draft Support

The package targets Draft 2020-12 as its primary draft and supports older drafts via the schema's $schema keyword. Conformance baseline against the official JSON Schema Test Suite at v0.1.0:

Draft        Mode               Test-suite pass rate
Draft 4      read-only          608 / 616  (98.7%)
Draft 6      read-only          829 / 835  (99.3%)
Draft 7      read-only          917 / 923  (99.3%)
Draft 2019-09  read-only        1239 / 1251 (99.0%)
Draft 2020-12  read + write     1274 / 1291 (98.7%)  ← primary target

"Read-only" means the package compiles and validates schemas authored against that draft; the schema generator (Generate, GenerateBytes, FromType) always emits Draft 2020-12 output. A schema's effective draft is determined by (in order): the $schema keyword at the schema root; the value passed via WithDefaultDraft; DraftDefault.

Type Mapping (Schema Generation)

Generate walks a Go value (or reflect.Type via FromType) and emits a JSON Schema describing it.

Go kind                          Generated schema
bool                             {"type": "boolean"}
intN, uintN                      {"type": "integer"} (with min/max on width)
float32, float64                 {"type": "number"}
string                           {"type": "string"}
[]byte, [N]byte                  {"type": "string", "contentEncoding": "base64"}
[]T, [N]T                        {"type": "array", "items": <T>}
map[string]V                     {"type": "object", "additionalProperties": <V>}
struct                           {"type": "object", "properties": {...}, "required": [...]}
time.Time                        {"type": "string", "format": "date-time"}
time.Duration                    {"type": "integer"} or "format": "duration" via [WithGenerateDurationAsString]
*T                               <T> by default; anyOf:[null, T] under [WithGenerateNullablePointers]
interface{} / any                {} by default; configurable via [WithGenerateInterfaceAsAny]
json.Number                      {"type": ["number", "string"]}
json.RawMessage                  {} (any)
encoding.TextMarshaler           {"type": "string"}
chan / func / unsafe.Pointer     generation error

Self-referential and mutually recursive types are emitted via $defs entries with internal $ref links. Custom emitters (registered via WithCustomEmitter) override the default kind-based mapping for a specific Go type.

Struct Tags

Two tags drive schema generation:

  • "json" — standard library semantics: property name, "omitempty" (excluded from "required"), and "-" (skipped).
  • "jsonschema" — schema-specific options.

Example:

type User struct {
    Name  string `json:"name" jsonschema:"required,minLength=1,maxLength=100"`
    Email string `json:"email" jsonschema:"required,format=email"`
    Age   int    `json:"age,omitempty" jsonschema:"minimum=0,maximum=150"`
    Roles []string `json:"roles" jsonschema:"minItems=1,uniqueItems,enum=admin|editor|viewer"`
}

Options are comma-separated; multi-value options use pipe (`enum=a|b|c`). See the README for the complete option vocabulary.

Strict Modes

Two independent strictness modes are supported:

  • WithMetaSchemaValidation — at compile time, validate each schema against its declared $schema meta-schema. Catches typos and malformed keyword values. Default: off (recommended for CI).
  • WithFormatAssertion — at validation time, treat the "format" keyword as an assertion rather than an annotation. Default: annotation-only, matching the Draft 2020-12 specification's default vocabulary set.

Output Formats

Result.Output renders a validation result in any of the four formats from Draft 2020-12 §12: OutputFlag, OutputBasic, OutputDetailed, and OutputVerbose. The wire shape matches the spec's output meta-schema (also embedded and exposed via OutputMetaSchema) and is suitable for sending over the network or feeding into downstream tools.

Error Handling

The package surfaces five typed errors, each carrying structured fields and supporting errors.Is / errors.As:

  • *CompileError — schema-document problems (malformed JSON, bad keyword shape, unknown vocabulary, ref resolution failure).
  • *ValidationError — assertion failure during validation. Multi-error unwrap (Go 1.20+) walks nested causes from compound applicators (anyOf, oneOf, allOf, $ref, ...). Switch on [ValidationError.Keyword] for stable error classification.
  • *RefError — $ref or $dynamicRef cannot be resolved.
  • *LoaderError — a Loader returned an I/O / network error.
  • *FormatError — a value with a "format" keyword failed its validator while format assertion is enabled.

Pointer-typed sentinels (ErrCompile, ErrValidation, ErrRef, ErrLoader, ErrFormat) match instances of their concrete error type via errors.Is. Specific failure conditions are surfaced via the package-level sentinels in [errors.go]: ErrUnknownDraft, ErrUnknownKeyword, ErrUnknownFormat, ErrRefCycle, ErrMaxRefDepth, ErrMaxValidationDepth, ErrInstanceTooLarge, ErrLoaderRejected, ErrSchemaNotCompiled, ErrValidationFailed, ErrNilReader, ErrUnsupportedSchemaShape. Multi-format adapters add ErrInvalidYAML and ErrInvalidTOML.

RenderError produces a human-readable error string with the (forward- looking) signature for source-line-pointer formatting.

Multi-Format Input

In addition to JSON, the package can load schemas and instances written as JSONC, YAML, or TOML via the LoadJSONC, LoadYAML, LoadTOML, ValidateJSONC, ValidateYAML, and ValidateTOML entry points. These adapters delegate to go-rotini/jsonc, go-rotini/yaml, and go-rotini/toml; numeric literals are preserved via encoding/json.Number so number-precision keywords (multipleOf, minimum, maximum, const) evaluate against the original wire form. Multi-format support is part of the main package — there is no dedicated sub-module to import.

Compatibility With encoding/json

The standard library has no schema layer, so there is no "drop-in" surface to mirror. The two libraries integrate at the boundary instead:

OpenAPI 3.1

The VocabOAS vocabulary and the OASDialectURL meta-schema are registered unconditionally so OpenAPI 3.1 schemas compile cleanly out of the box. A schema declaring OASDialectURL as $schema runs against Draft 2020-12 plus the OAS-specific annotation keywords (discriminator, xml, externalDocs, example).

Bowtie Connector

A standards-conformance connector for the Bowtie cross-implementation JSON Schema test harness ships in the bowtie/ sub-package as a `package main` adapter that speaks Bowtie's stdin/stdout protocol.

DoS Protection

The package ships with four independent guards against adversarial input: WithMaxRefDepth (default 100) caps $ref hop depth per keyword; WithMaxValidationDepth (alias WithMaxDepth, default 1000) caps recursion into nested instances; WithMaxInstanceSize (alias WithMaxDocumentSize) caps instance bytes before parsing; the compiler detects ref cycles at compile time and turns them into lazy edges so they cannot stack-overflow the validator.

Index

Examples

Constants

View Source
const (
	// VocabCore identifies the meta-keywords vocabulary
	// ($schema, $id, $ref, $dynamicRef, $defs, $anchor, $dynamicAnchor,
	// $comment, $vocabulary).
	VocabCore = "https://json-schema.org/draft/2020-12/vocab/core"
	// VocabApplicator identifies the keywords that apply subschemas
	// (allOf, anyOf, properties, items, ...).
	VocabApplicator = "https://json-schema.org/draft/2020-12/vocab/applicator"
	// VocabUnevaluated identifies unevaluatedItems / unevaluatedProperties.
	VocabUnevaluated = "https://json-schema.org/draft/2020-12/vocab/unevaluated"
	// VocabValidation identifies the assertion keywords (type, enum, ...).
	VocabValidation = "https://json-schema.org/draft/2020-12/vocab/validation"
	// VocabFormatAnnot identifies the annotation-only flavor of the
	// "format" keyword (the Draft 2020-12 default).
	VocabFormatAnnot = "https://json-schema.org/draft/2020-12/vocab/format-annotation"
	// VocabFormatAssert identifies the assertion flavor of "format"
	// (opt-in via [WithFormatAssertion]).
	VocabFormatAssert = "https://json-schema.org/draft/2020-12/vocab/format-assertion"
	// VocabContent identifies contentEncoding / contentMediaType /
	// contentSchema.
	VocabContent = "https://json-schema.org/draft/2020-12/vocab/content"
	// VocabMetaData identifies title / description / default / examples /
	// readOnly / writeOnly / deprecated.
	VocabMetaData = "https://json-schema.org/draft/2020-12/vocab/meta-data"
	// VocabOAS identifies the OpenAPI 3.1 base vocabulary that ships with
	// the OAS dialect ([OASDialectURL]). It contributes the four annotation-
	// only keywords [discriminator], [xml], [externalDocs], and [example].
	// The package registers this vocabulary unconditionally so OpenAPI 3.1
	// schemas compile cleanly whether or not their $schema points at the
	// OAS dialect.
	VocabOAS = "https://spec.openapis.org/oas/3.1/vocab/base"
)

Standard vocabulary URIs (Draft 2020-12).

View Source
const (
	// OASDialectURL is the canonical URI of the OpenAPI 3.1 Schema Object
	// dialect's meta-schema. A schema declaring this URL as $schema opts
	// into Draft 2020-12 plus the [VocabOAS] vocabulary.
	OASDialectURL = "https://spec.openapis.org/oas/3.1/dialect/base"
	// OASBaseSchemaURL is the canonical $id of the upstream OpenAPI 3.1
	// document schema (the schema that validates OpenAPI documents
	// themselves, distinct from the dialect that those schemas use).
	OASBaseSchemaURL = "https://spec.openapis.org/oas/3.1/schema/2022-10-07"
)

OAS dialect identifiers for OpenAPI 3.1.

View Source
const DraftDefault = Draft202012

DraftDefault is the draft used when neither the schema's $schema keyword nor the caller's WithDefaultDraft specifies one.

Variables

View Source
var (
	// ErrCompile matches any [*CompileError].
	ErrCompile = &CompileError{}
	// ErrValidation matches any [*ValidationError].
	ErrValidation = &ValidationError{}
	// ErrRef matches any [*RefError].
	ErrRef = &RefError{}
	// ErrLoader matches any [*LoaderError].
	ErrLoader = &LoaderError{}
	// ErrFormat matches any [*FormatError].
	ErrFormat = &FormatError{}

	// ErrUnknownDraft indicates a draft selector that the package does not
	// recognize (typically [DraftUnknown] passed to a Draft-only API).
	ErrUnknownDraft = errors.New("jsonschema: unknown draft")
	// ErrUnknownKeyword indicates a keyword that is not registered for the
	// active draft and [WithStrictKeywords] is enabled.
	ErrUnknownKeyword = errors.New("jsonschema: unknown keyword")
	// ErrUnknownFormat indicates a "format" value with no registered
	// validator while the unknown-format policy is [UnknownFormatError].
	// Surfaced as the [FormatError.Cause] inside the wrapping
	// [*ValidationError.Cause].
	ErrUnknownFormat = errors.New("jsonschema: unknown format")
	// ErrRefCycle indicates a cyclic $ref chain that the compiler could
	// not turn into a lazy edge. Reserved for future use; v0.1's compile
	// path resolves every cycle into a lazy edge bounded at run time by
	// [WithMaxRefDepth] (which surfaces as [ErrMaxRefDepth]).
	ErrRefCycle = errors.New("jsonschema: ref cycle detected")
	// ErrMaxRefDepth indicates a single keyword evaluation followed more
	// than [WithMaxRefDepth] hops.
	ErrMaxRefDepth = errors.New("jsonschema: max ref depth exceeded")
	// ErrMaxValidationDepth indicates the validator recursed past
	// [WithMaxValidationDepth] levels into nested instances.
	ErrMaxValidationDepth = errors.New("jsonschema: max validation depth exceeded")
	// ErrMaxKeyCount indicates an object instance had more keys than the
	// [WithMaxKeyCount] cap allows.
	ErrMaxKeyCount = errors.New("jsonschema: max key count exceeded")
	// ErrInstanceTooLarge indicates an instance document larger than
	// [WithMaxInstanceSize] was rejected before parsing.
	ErrInstanceTooLarge = errors.New("jsonschema: instance exceeds size limit")
	// ErrLoaderRejected indicates a [Loader] declined a URI scheme (e.g.
	// the default chain rejecting http:// without explicit opt-in).
	ErrLoaderRejected = errors.New("jsonschema: loader rejected URI scheme")
	// ErrSchemaNotCompiled indicates a [*Schema] method was called on a
	// nil receiver, or on a value that was not produced by the compiler
	// (e.g. a zero-value [Schema] literal).
	ErrSchemaNotCompiled = errors.New("jsonschema: schema not compiled")
	// ErrValidationFailed is returned when validation produced no
	// structured [*ValidationError] but the instance was nevertheless
	// rejected.
	ErrValidationFailed = errors.New("jsonschema: validation failed")
	// ErrNilReader indicates a nil [io.Reader] was passed to
	// [*Schema.ValidateReader].
	ErrNilReader = errors.New("jsonschema: nil reader")
	// ErrUnsupportedSchemaShape indicates a schema slot held a value the
	// compiler/runtime cannot evaluate (neither a JSON object nor a
	// boolean schema). Surfaced as the [CompileError.Cause].
	ErrUnsupportedSchemaShape = errors.New("jsonschema: unsupported schema shape")
)

Sentinel errors. The pointer-typed sentinels (ErrCompile, ErrValidation, ErrRef, ErrLoader, ErrFormat) match instances of their concrete error type via errors.Is; the package-level errors.New sentinels surface specific failure conditions that do not warrant a structured type.

View Source
var (
	// ErrInvalidYAML indicates a structural problem in YAML input that the
	// adapter could not convert to a JSON-compatible value (e.g. unresolved
	// aliases, cyclic merges, or multi-document streams).
	ErrInvalidYAML = errors.New("jsonschema: invalid yaml")
	// ErrInvalidTOML indicates a structural problem in TOML input the
	// adapter could not represent as a JSON-compatible value (e.g.
	// integer literal out of range, malformed AST shape).
	ErrInvalidTOML = errors.New("jsonschema: invalid toml")
)

Sentinel errors returned by the multi-format adapters. All errors emitted by these adapters wrap one of these so callers can match via errors.Is.

Functions

func GenerateBytes

func GenerateBytes(v any, opts ...GenerateOption) ([]byte, error)

GenerateBytes returns the JSON-encoded schema for v. Equivalent to Generate followed by Schema.MarshalJSON but exposes the bytes directly for callers that want the wire form (e.g. to write to disk).

func MetaSchemaBytes

func MetaSchemaBytes(d Draft) ([]byte, error)

MetaSchemaBytes returns the JSON bytes of the canonical meta-schema for d. The bytes are a slice into the package's embedded copy and must be treated as read-only; callers that need a mutable buffer should copy.

Returns ErrUnknownDraft for DraftUnknown or any unrecognized value.

func MetaSchemaURL

func MetaSchemaURL(d Draft) string

MetaSchemaURL returns the canonical URL of the meta-schema for d. It is equivalent to d.MetaSchemaURL() and re-exported as a package-level function for symmetry with MetaSchemaBytes.

func MustValidateTo

func MustValidateTo[T any](schema *Schema, instanceJSON []byte, opts ...Option) T

MustValidateTo is the panic-on-error variant of ValidateTo. Intended for package-init use of static, well-known instances; tests and one-shot CLIs where a malformed input is a programming error.

func RenderError

func RenderError(schemaSrc, instanceSrc []byte, err error, color ...bool) string

RenderError produces a human-readable error string.

When err is a *ValidationError (single or multi-cause), the output emits one structured block per leaf cause with the schema keyword location, the instance pointer, the human-readable message, and — when schemaSrc / instanceSrc are non-empty and the JSON pointer can be resolved against them — a snippet of the offending source line with a caret pointer. When err is a *CompileError, only the schema side is rendered. For other error types the function falls through to err.Error().

Set color to true to wrap error text and the caret in ANSI escape sequences (red for errors; bold for the line snippet header). Color is off by default.

Programmatic callers should not parse the returned string — use errors.Is / errors.As against the typed errors (*CompileError, *ValidationError, *RefError, *LoaderError, *FormatError) and switch on [ValidationError.Keyword] for stable classification.

func ValidateTo

func ValidateTo[T any](schema *Schema, instanceJSON []byte, opts ...Option) (T, error)

ValidateTo validates instanceJSON against schema and returns the decoded value of type T on success, or the validation error on failure. It is the generic counterpart to *Schema.ValidateAndUnmarshal.

Types

type Annotation

type Annotation struct {
	// KeywordLocation is the JSON Pointer to the keyword in the schema that
	// produced the annotation (e.g. "/properties/name/title").
	KeywordLocation string
	// AbsoluteKeywordLocation is the resolved-URL form of KeywordLocation;
	// empty unless the schema was loaded from a remote URI.
	AbsoluteKeywordLocation string
	// InstanceLocation is the JSON Pointer to the instance value that the
	// annotation describes.
	InstanceLocation string
	// Keyword is the bare keyword name (e.g. "title").
	Keyword string
	// Value is the annotation payload (the keyword's value, by default).
	Value any
}

Annotation is a successful keyword annotation produced during validation.

type ChainLoader

type ChainLoader []Loader

ChainLoader tries each Loader in order and returns the first non-rejected result. A rejection is any error that wraps ErrLoaderRejected; any other error short-circuits the chain so that a real I/O failure surfaces.

func (ChainLoader) Load

func (c ChainLoader) Load(uri string) ([]byte, error)

Load implements Loader.

type CompileError

type CompileError struct {
	// KeywordLocation is the JSON Pointer (or URL with fragment) to the
	// position in the schema where the problem was found.
	KeywordLocation string
	// Message is a human-readable description of the problem.
	Message string
	// Cause carries an optional underlying error (e.g. a Loader I/O
	// failure wrapped by the compiler when resolving an external $ref).
	Cause error
}

CompileError is returned when a schema document is malformed, references a non-existent vocabulary, or violates a keyword's value constraints (e.g. minLength is a string instead of a non-negative integer).

func (*CompileError) Error

func (e *CompileError) Error() string

Error returns a human-readable representation.

func (*CompileError) Is

func (e *CompileError) Is(target error) bool

Is reports whether target is a *CompileError sentinel.

func (*CompileError) Unwrap

func (e *CompileError) Unwrap() error

Unwrap returns the underlying cause, if any.

type CompileOption

type CompileOption func(*compileOptions)

CompileOption configures how Compile, CompileURL, CompileValue, and the Compiler type process schema documents. Apply options at compiler construction (or per-call); they shape draft selection, ref loading, strictness, and meta-schema handling.

func WithBaseURI

func WithBaseURI(uri string) CompileOption

WithBaseURI sets the base URI for the root schema when the schema does not declare an $id. Useful when compiling a schema fragment loaded from a known URL so that relative refs resolve correctly.

func WithDefaultDraft

func WithDefaultDraft(d Draft) CompileOption

WithDefaultDraft sets the draft used when a schema's $schema keyword is absent. Default: DraftDefault (Draft 2020-12).

func WithLoader

func WithLoader(l Loader) CompileOption

WithLoader sets the Loader used to fetch external $refs. Default: a ChainLoader of the embedded meta-schemas plus an HTTPS-only HTTPLoader.

func WithLoaderTrace

func WithLoaderTrace(w io.Writer) CompileOption

WithLoaderTrace writes one line per Loader fetch to w. Useful for diagnosing $ref resolution. Default: nil (no trace).

func WithMaxRefDepth

func WithMaxRefDepth(n int) CompileOption

WithMaxRefDepth limits how many $ref hops a single keyword evaluation may follow. Guards against pathological recursive schemas. Default: 100.

func WithMetaSchemaValidation

func WithMetaSchemaValidation(b bool) CompileOption

WithMetaSchemaValidation enables compile-time validation of each schema against its declared meta-schema. Catches typos and structural errors early. Default: false (off for performance; recommended in CI).

When the schema declares the OpenAPI 3.1 dialect URL (OASDialectURL) the embedded dialect meta-schema is used; otherwise the standard meta-schema for the schema's draft is used.

func WithRefCollisionPolicy

func WithRefCollisionPolicy(p RefCollisionPolicy) CompileOption

WithRefCollisionPolicy controls behavior when two compiled documents declare the same $id. Default: RefCollisionError (compile fails).

In v0.1, only RefCollisionError is wired — other policy values are reserved for v0.2 and currently behave the same as RefCollisionError.

func WithStrict

func WithStrict() CompileOption

WithStrict is a zero-argument shorthand for WithStrictKeywords(true). Provided for consistency with the rotini sister packages.

func WithStrictKeywords

func WithStrictKeywords(b bool) CompileOption

WithStrictKeywords causes unknown keywords to raise a *CompileError at compile time. Default: unknown keywords are kept as annotations and ignored at validation.

type Compiler

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

Compiler holds compile-time configuration and a cache of compiled remote schemas. Reuse a Compiler when compiling many schemas that share remote references — the cache amortizes loader I/O.

A Compiler is safe for concurrent use after construction. The cache is keyed by absolute URI; concurrent *Compiler.CompileURL calls for the same URI share a single fetch+compile pipeline via an inline single-flight.

func NewCompiler

func NewCompiler(opts ...CompileOption) *Compiler

NewCompiler returns a fresh *Compiler with the supplied options applied.

Example

ExampleNewCompiler reuses one Compiler across multiple Compile calls so that any external schemas referenced via $ref are fetched once and cached.

package main

import (
	"fmt"

	"github.com/go-rotini/jsonschema"
)

func main() {
	c := jsonschema.NewCompiler()
	a, _ := c.Compile([]byte(`{"type":"string"}`))
	b, _ := c.Compile([]byte(`{"type":"integer"}`))
	r1, _ := a.Validate([]byte(`"x"`))
	r2, _ := b.Validate([]byte(`5`))
	fmt.Println(r1.Valid, r2.Valid)
}
Output:
true true

func (*Compiler) AddResource

func (c *Compiler) AddResource(uri string, schemaJSON []byte) error

AddResource registers a schema document under uri so subsequent compilations resolving that URI can find it without invoking the Loader. The bytes are validated as JSON; the document is parsed lazily when first referenced.

func (*Compiler) Compile

func (c *Compiler) Compile(schemaJSON []byte) (*Schema, error)

Compile parses and compiles schemaJSON. The result is cached by $id so subsequent calls referencing the same document via $ref short-circuit.

func (*Compiler) CompileURL

func (c *Compiler) CompileURL(uri string) (*Schema, error)

CompileURL fetches uri via the compiler's loader and compiles the resulting bytes. Concurrent calls for the same URI share a single fetch+compile pipeline (single-flight); the result is cached so subsequent calls return immediately.

func (*Compiler) CompileValue

func (c *Compiler) CompileValue(v any) (*Schema, error)

CompileValue compiles an already-decoded Go value (an `any` produced by json.Unmarshal — typically a map[string]any tree).

func (*Compiler) MustCompile

func (c *Compiler) MustCompile(schemaJSON []byte) *Schema

MustCompile is the panic-on-error variant of *Compiler.Compile.

func (*Compiler) MustCompileURL

func (c *Compiler) MustCompileURL(uri string) *Schema

MustCompileURL is the panic-on-error variant of *Compiler.CompileURL.

func (*Compiler) MustCompileValue

func (c *Compiler) MustCompileValue(v any) *Schema

MustCompileValue is the panic-on-error variant of *Compiler.CompileValue.

type Draft

type Draft int

Draft enumerates the JSON Schema specification drafts the package recognizes. Use DraftDefault when you need the package's preferred draft; pin to a specific constant when interacting with legacy schemas.

const (
	// DraftUnknown is the zero value; it indicates "no draft selected".
	DraftUnknown Draft = iota
	// Draft4 corresponds to JSON Schema Draft 4 (2013).
	Draft4
	// Draft6 corresponds to JSON Schema Draft 6 (2017).
	Draft6
	// Draft7 corresponds to JSON Schema Draft 7 (2018).
	Draft7
	// Draft201909 corresponds to JSON Schema Draft 2019-09.
	Draft201909
	// Draft202012 corresponds to JSON Schema Draft 2020-12.
	Draft202012
)

Recognized drafts.

func DraftFromMetaSchemaURL

func DraftFromMetaSchemaURL(url string) Draft

DraftFromMetaSchemaURL maps a meta-schema URL back to a Draft constant. Both http:// and https:// variants of the legacy URLs are accepted, and trailing # fragments are tolerated. Returns DraftUnknown when no draft matches.

The OpenAPI 3.1 dialect URL (OASDialectURL) maps to Draft202012 — the dialect is a strict superset of Draft 2020-12, so resolving it to Draft 2020-12 wires the schema into the standard 2020-12 evaluator graph and lets the OAS-specific annotation keywords ride along.

func (Draft) DefsKeyword

func (d Draft) DefsKeyword() string

DefsKeyword returns the subschema-container keyword name for d. Drafts 4 through 7 used "definitions"; Draft 2019-09 introduced "$defs".

func (Draft) IDKeyword

func (d Draft) IDKeyword() string

IDKeyword returns the schema-identifier keyword name for d. Draft 4 spelled it as "id" (no leading $); subsequent drafts use "$id".

func (Draft) MetaSchemaURL

func (d Draft) MetaSchemaURL() string

MetaSchemaURL returns the canonical URL of the meta-schema that defines d. Legacy drafts (4/6/7) historically used http://; the modern drafts use https://. Returns the empty string for DraftUnknown.

func (Draft) String

func (d Draft) String() string

String returns a human-readable label for d (e.g. "Draft 2020-12").

type EmbedLoader

type EmbedLoader struct {
	// FS is the embedded filesystem to serve from. Required.
	FS embed.FS
}

EmbedLoader wraps an embed.FS so schemas bundled into the binary can be referenced via embed:// URIs (e.g. embed://schemas/user.json resolves to the FS path "schemas/user.json").

func (EmbedLoader) Load

func (l EmbedLoader) Load(uri string) ([]byte, error)

Load implements Loader.

type FileLoader

type FileLoader struct {
	// Root is the directory file:// URIs resolve against. Required; an
	// empty Root disables the loader entirely.
	Root string
}

FileLoader resolves file:// URIs against the local filesystem. The Root field is mandatory: any path that escapes Root (via .., absolute paths outside Root, etc.) is rejected. When Root is the empty string, FileLoader refuses every URI (returning ErrLoaderRejected) — callers must opt in explicitly to file-system access.

func (FileLoader) Load

func (l FileLoader) Load(uri string) ([]byte, error)

Load implements Loader.

type FormatError

type FormatError struct {
	// Format is the format name (e.g. "date-time", "uuid").
	Format string
	// Value is the offending source value.
	Value string
	// Cause carries an optional underlying parser/validator error.
	Cause error
}

FormatError is surfaced when a value with a "format" keyword fails its associated format validator (and format assertion is enabled).

func (*FormatError) Error

func (e *FormatError) Error() string

Error returns a human-readable representation.

func (*FormatError) Is

func (e *FormatError) Is(target error) bool

Is reports whether target is a *FormatError sentinel.

func (*FormatError) Unwrap

func (e *FormatError) Unwrap() error

Unwrap returns the underlying cause, if any.

type FormatValidator

type FormatValidator func(string) error

FormatValidator validates a string against a named format. It returns nil when the value conforms; otherwise it returns a non-nil error (typically a *FormatError but any error is accepted from custom validators).

type GenerateOption

type GenerateOption func(*generateOptions)

GenerateOption configures the schema generator. Pass options to Generate, GenerateBytes, FromType, or NewGenerator; later constructors return a reusable *Generator that amortizes option parsing across many types.

func WithCustomEmitter

func WithCustomEmitter[T any](fn func(reflect.Type) *Schema) GenerateOption

WithCustomEmitter registers a function that emits the schema for values of type T, overriding the default kind-based mapping.

func WithDocReader

func WithDocReader(src fs.FS) GenerateOption

WithDocReader extracts Go doc comments from src files (passed as an fs.FS) and uses them as the description annotation for matching struct fields. The generator parses the source files lazily on first use; if the FS contains no Go sources the option is a no-op.

func WithGenerateAdditionalPropertiesFalse

func WithGenerateAdditionalPropertiesFalse(b bool) GenerateOption

WithGenerateAdditionalPropertiesFalse emits "additionalProperties": false on every generated struct schema. Default: false.

func WithGenerateDraft

func WithGenerateDraft(d Draft) GenerateOption

WithGenerateDraft sets the schema draft to emit. Default: Draft 2020-12.

func WithGenerateDurationAsString

func WithGenerateDurationAsString(b bool) GenerateOption

WithGenerateDurationAsString emits time.Duration as {"type":"string","format":"duration"} instead of integer nanoseconds.

func WithGenerateExpandedRefs

func WithGenerateExpandedRefs(b bool) GenerateOption

WithGenerateExpandedRefs inlines all referenced types instead of using $ref + $defs. Default: false.

func WithGenerateID

func WithGenerateID(id string) GenerateOption

WithGenerateID sets the $id of the generated root schema.

func WithGenerateInterfaceAsAny

func WithGenerateInterfaceAsAny(b bool) GenerateOption

WithGenerateInterfaceAsAny controls how interface{} / any is rendered. true (default): {} (any-value). false: error at generation time so the caller is forced to register a custom emitter.

func WithGenerateNullablePointers

func WithGenerateNullablePointers(b bool) GenerateOption

WithGenerateNullablePointers emits *T as anyOf:[null, T]. Default: false.

func WithGenerateOmitDescriptions

func WithGenerateOmitDescriptions(b bool) GenerateOption

WithGenerateOmitDescriptions suppresses extracting Go doc comments as description strings. Default: false.

func WithGenerateOrderedProperties

func WithGenerateOrderedProperties(b bool) GenerateOption

WithGenerateOrderedProperties controls whether emitted struct schemas preserve Go field declaration order. When true (default), `properties` keys are emitted in declaration order. When false, the generator emits a plain map[string]any whose key order is unspecified.

func WithGenerateSchemaDeclaration

func WithGenerateSchemaDeclaration(b bool) GenerateOption

WithGenerateSchemaDeclaration emits "$schema" on the root of generated schemas. Default: true.

type Generator

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

Generator is the configurable schema-from-Go-types worker. Reuse a single Generator when you have many types to describe with the same options; option parsing happens once at construction.

A Generator is safe for concurrent use: it carries only immutable configuration plus a lazy doc-comment cache that is built on first use behind a sync.Once.

func NewGenerator

func NewGenerator(opts ...GenerateOption) *Generator

NewGenerator returns a fresh *Generator with the supplied options applied. Options that are unset inherit their documented defaults (matching the package-level Generate entry points).

func (*Generator) FromType

func (g *Generator) FromType(t reflect.Type) (*Schema, error)

FromType generates the schema for the named reflect.Type.

func (*Generator) Generate

func (g *Generator) Generate(v any) (*Schema, error)

Generate generates the schema for the runtime type of v.

func (*Generator) GenerateBytes

func (g *Generator) GenerateBytes(v any) ([]byte, error)

GenerateBytes returns the JSON-encoded schema for the runtime type of v.

func (*Generator) MustGenerate

func (g *Generator) MustGenerate(v any) *Schema

MustGenerate is the panic-on-error variant of *Generator.Generate.

type HTTPLoader

type HTTPLoader struct {
	// Client is the http.Client used to perform requests. nil falls back
	// to a per-call client built from Timeout.
	Client *http.Client
	// Timeout caps the duration of a single Load. Default: 10 s.
	Timeout time.Duration
	// MaxBodySize caps the response body size in bytes. Default: 10 MiB.
	MaxBodySize int64
	// AllowHTTP, when true, permits http:// URLs in addition to https://.
	// Default: false.
	AllowHTTP bool
	// Cache is the in-memory cache TTL for successful responses. 0 disables
	// caching (the default).
	Cache time.Duration
	// RequestDecorator is an optional hook invoked on every outbound
	// request — useful for adding authentication headers or API tokens.
	RequestDecorator func(*http.Request)
	// contains filtered or unexported fields
}

HTTPLoader resolves http:// and https:// URIs over the network. It is HTTPS-only by default; AllowHTTP must be set to true to follow plain http:// URLs. Concurrent requests for the same URI share a single network round-trip via an inline single-flight.

Set the exported fields at construction; treat the value as read-only afterwards. Pass *HTTPLoader (not a copy) so the cache and in-flight state are shared across goroutines.

func (*HTTPLoader) Load

func (l *HTTPLoader) Load(uri string) ([]byte, error)

Load implements Loader.

type Keyword

type Keyword interface {
	// Name returns the keyword as it appears in a schema (e.g. "minLength").
	Name() string
	// SinceDraft returns the first draft in which this keyword exists.
	SinceDraft() Draft
	// RetiredInDraft returns the draft in which this keyword was removed,
	// or [DraftUnknown] if the keyword is still current in 2020-12.
	RetiredInDraft() Draft
}

Keyword is the metadata-only interface every JSON Schema keyword satisfies. It carries the keyword's name and the Draft range in which it is recognized. The compiler uses these values to route a schema member to its evaluator; tooling uses them to enumerate the keyword set active for a given draft via KeywordsForDraft / LookupKeyword.

func KeywordsForDraft

func KeywordsForDraft(d Draft) []Keyword

KeywordsForDraft returns every keyword that is recognized when the active draft is d, deduplicated by name. A keyword belongs to the result iff its [Keyword.SinceDraft] is ≤ d and its [Keyword.RetiredInDraft] is either DraftUnknown or > d. Returns nil for DraftUnknown.

func LookupKeyword

func LookupKeyword(name string, d Draft) (Keyword, bool)

LookupKeyword finds a keyword by name within the active set for draft d. Returns ok=false when the keyword is not recognized by the package, when it does not exist in the requested draft, or when d is DraftUnknown.

type KeywordBinding

type KeywordBinding struct {
	// Name is the keyword identifier (e.g. "minLength", "$ref").
	Name string
	// Location is the JSON Pointer of the keyword in the source schema.
	Location string
	// RawValue is the parsed value of the keyword (json.Unmarshal'd).
	// May be a map[string]any, []any, json.Number, string, bool, or nil.
	RawValue any
}

KeywordBinding is the public projection of one keyword binding extracted at compile time. Returned by *Schema.Bindings for introspection and metadata-only consumers.

type Loader

type Loader interface {
	// Load returns the schema bytes for the given URI, or an error
	// describing why the URI could not be served. Implementations that do
	// not handle a URI should return [ErrLoaderRejected] (wrapped in a
	// [*LoaderError]) so a [ChainLoader] can fall through to the next.
	Load(uri string) ([]byte, error)
}

Loader fetches the contents of a schema referenced by URI. Implementations must be safe for concurrent use; the compiler may invoke a single Loader from multiple goroutines while resolving a graph of refs.

func DefaultLoader

func DefaultLoader() Loader

DefaultLoader returns the package's default Loader: a ChainLoader containing the embedded standard meta-schemas (so meta-schema refs resolve without network access) followed by an HTTPS-only HTTPLoader with sane defaults.

The returned Loader is shared across calls; callers that need different behavior should build their own ChainLoader.

type LoaderError

type LoaderError struct {
	// URI identifies the resource that failed to load.
	URI string
	// Cause is the underlying I/O / network / parse error.
	Cause error
}

LoaderError wraps an underlying I/O / network error from a Loader.

func (*LoaderError) Error

func (e *LoaderError) Error() string

Error returns a human-readable representation.

func (*LoaderError) Is

func (e *LoaderError) Is(target error) bool

Is reports whether target is a *LoaderError sentinel.

func (*LoaderError) Unwrap

func (e *LoaderError) Unwrap() error

Unwrap returns the underlying cause, if any.

type MapItem

type MapItem struct {
	// Key is the JSON object member name.
	Key string
	// Value is the decoded value for this member.
	Value any
}

MapItem is a single key-value pair within a MapSlice. JSON object keys are always strings, so [MapItem.Key] is typed as string.

type MapLoader

type MapLoader map[string][]byte

MapLoader is a Loader backed by a static map of URI to bytes. It is useful in tests, in Compiler.AddResource, and as the storage for the embedded standard meta-schemas in the default loader chain.

Lookups tolerate a trailing # on the requested URI (so an `$id` that ends in `#` matches an entry stored without it, and vice versa).

func (MapLoader) Load

func (m MapLoader) Load(uri string) ([]byte, error)

Load implements Loader.

type MapSlice

type MapSlice []MapItem

MapSlice is an ordered slice of key-value pairs. The schema generator emits MapSlice for properties / definitions / $defs to preserve Go declaration order on round-trip; the validator accepts MapSlice (alongside map[string]any) as the in-memory representation of a JSON object.

type Number

type Number = json.Number

Number is an alias for encoding/json.Number. It preserves the original source text of a JSON number so that precision-sensitive keywords such as multipleOf can compare against the wire form rather than a lossy float64.

The alias lets values flow between this package, the standard library, and the rotini sister packages without a wrapper type at every boundary.

type Option

type Option func(*runOptions)

Option configures a single validation call. Applied to *Schema Validate methods and the package-level Validate function.

Options are evaluated left-to-right; later options override earlier ones for the same field. Most callers will mix WithFormatAssertion, WithStopOnFirstError, WithMaxValidationDepth, and WithCustomFormat.

func WithCollectAnnotations

func WithCollectAnnotations(b bool) Option

WithCollectAnnotations controls whether passing keyword annotations are accumulated in [Result.Annotations]. Default: true.

func WithContentAssertion

func WithContentAssertion(b bool) Option

WithContentAssertion enables contentSchema as an assertion (default: annotation only).

func WithCustomFormat

func WithCustomFormat(name string, fn func(string) error) Option

WithCustomFormat registers a format validator for the given format name.

func WithFormatAssertion

func WithFormatAssertion(b bool) Option

WithFormatAssertion enables "format" as an assertion (default: annotation only). When true, format-validator failures become validation errors.

func WithMaxDepth

func WithMaxDepth(n int) Option

WithMaxDepth is a sister-package alias for WithMaxValidationDepth.

func WithMaxDocumentSize

func WithMaxDocumentSize(n int) Option

WithMaxDocumentSize is a sister-package alias for WithMaxInstanceSize. Both options write to the same underlying field; later calls win.

func WithMaxErrors

func WithMaxErrors(n int) Option

WithMaxErrors caps the number of ValidationError entries collected. After the cap is reached, validation continues (unless WithStopOnFirstError is also set) but additional errors are dropped. Default: 0 (unlimited).

func WithMaxInstanceSize

func WithMaxInstanceSize(n int) Option

WithMaxInstanceSize rejects instance documents larger than n bytes before parsing. n <= 0 disables the limit. Default: 0 (no limit). Returns ErrInstanceTooLarge when an instance exceeds the cap.

func WithMaxKeyCount

func WithMaxKeyCount(n int) Option

WithMaxKeyCount caps the number of object keys the validator will visit on any single object instance. When an object has more keys than n, validation surfaces a *ValidationError with Keyword "$maxKeyCount" and Cause ErrMaxKeyCount. Default: 0 (unlimited).

Mitigates DoS via instances with millions of object keys. WithMaxInstanceSize transitively bounds the same attack surface; WithMaxKeyCount is a finer-grain guard for cases where the instance bytes are not the limiting factor (e.g. repeated short keys).

func WithMaxValidationDepth

func WithMaxValidationDepth(n int) Option

WithMaxValidationDepth limits recursion into nested objects/arrays to guard against unbounded recursive schemas with adversarial instances. Default: 1000.

func WithReadOnly

func WithReadOnly(b bool) Option

WithReadOnly enables direction-aware validation in the "read" direction (the value is being produced as output). Required properties whose schema is annotated `"writeOnly": true` are not enforced because such fields should not appear in output documents.

func WithStopOnFirstError

func WithStopOnFirstError(b bool) Option

WithStopOnFirstError short-circuits validation as soon as the first error is found. Equivalent to selecting OutputFlag at the collector level.

Note: applicator branches (anyOf, oneOf, if/then/else) always evaluate every alternative regardless of this option, because the keyword's outcome depends on knowing which branches passed. Short-circuiting applies only at the top-level evaluator and to sibling keywords within a single subschema.

func WithUnknownFormat

func WithUnknownFormat(p UnknownFormatPolicy) Option

WithUnknownFormat controls handling of unrecognized "format" names.

func WithWarningSink

func WithWarningSink(w io.Writer) Option

WithWarningSink installs a writer that receives diagnostic messages emitted during validation. v0.1 emits one line per unknown format under the UnknownFormatWarn policy, deduplicated within a single Validate call. Future warning-class diagnostics will write to the same sink. Default: nil (warnings dropped silently).

func WithWriteOnly

func WithWriteOnly(b bool) Option

WithWriteOnly enables direction-aware validation in the "write" direction (the value is being submitted as input). Required properties whose schema is annotated `"readOnly": true` are not enforced because such fields should not appear in input documents.

type OutputFormat

type OutputFormat int

OutputFormat selects the JSON shape produced by Result.Output per Draft 2020-12 §12.

const (
	// OutputFlag emits {"valid": true|false} only.
	OutputFlag OutputFormat = iota
	// OutputBasic emits a flat list of assertion outcomes.
	OutputBasic
	// OutputDetailed emits a nested tree mirroring the schema's applicator
	// structure, collapsing groups with no errors.
	OutputDetailed
	// OutputVerbose emits the full nested tree, including passing groups.
	OutputVerbose
)

Recognized output formats.

func (OutputFormat) String

func (f OutputFormat) String() string

String returns a human-readable label for f (e.g. "flag", "basic").

type RefCollisionPolicy

type RefCollisionPolicy int

RefCollisionPolicy controls behavior when two schema documents share the same $id within a single compiler.

const (
	// RefCollisionError aborts compilation on collision (default).
	RefCollisionError RefCollisionPolicy = iota
	// RefCollisionFirstWins keeps the first-registered document and
	// silently ignores subsequent collisions.
	RefCollisionFirstWins
	// RefCollisionLastWins replaces an earlier document with the later one.
	RefCollisionLastWins
)

Recognized ref-collision policies.

func (RefCollisionPolicy) String

func (p RefCollisionPolicy) String() string

String returns a human-readable label for p.

type RefError

type RefError struct {
	// Ref is the unresolved reference value as written in the schema.
	Ref string
	// BaseURI is the base URI in effect when resolution was attempted.
	BaseURI string
	// Cause carries an optional underlying error (e.g. a Loader fetch
	// failure or a JSON-Pointer syntax error).
	Cause error
}

RefError is returned when a $ref or $dynamicRef cannot be resolved against any in-scope schema resource.

func (*RefError) Error

func (e *RefError) Error() string

Error returns a human-readable representation.

func (*RefError) Is

func (e *RefError) Is(target error) bool

Is reports whether target is a *RefError sentinel.

func (*RefError) Unwrap

func (e *RefError) Unwrap() error

Unwrap returns the underlying cause, if any.

type Result

type Result struct {
	// Valid reports whether the instance validated successfully.
	Valid bool
	// Errors is the flat list of assertion failures (Basic-format equivalent).
	Errors []ValidationError
	// Annotations is the flat list of annotations produced by passing keywords.
	Annotations []Annotation
}

Result is the structured outcome of a validation run. It is returned by every *Schema Validate-family method; the Result.Output helper renders it into one of the four spec-defined output formats.

Callers should not construct a Result via a struct literal: future versions of this package may add fields and a literal-construction site would silently miss them. Always obtain a *Result from a *Schema Validate-family method.

func Validate

func Validate(schemaJSON, instanceJSON []byte, opts ...Option) (*Result, error)

Validate compiles schemaJSON and validates instanceJSON against it in one call. This is a convenience for one-shot use; callers performing multiple validations against the same schema should call Compile once and use *Schema.Validate to amortize compilation cost.

Example

ExampleValidate is the one-shot Compile + Validate convenience for callers that only validate one instance and don't need to retain the compiled *jsonschema.Schema.

package main

import (
	"fmt"

	"github.com/go-rotini/jsonschema"
)

func main() {
	res, err := jsonschema.Validate(
		[]byte(`{"type":"integer","minimum":0}`),
		[]byte(`-5`),
	)
	if err != nil {
		fmt.Println("err:", err)
		return
	}
	fmt.Println(res.Valid)
}
Output:
false

func ValidateJSONC

func ValidateJSONC(s *Schema, data []byte, opts ...Option) (*Result, error)

ValidateJSONC decodes data as JSONC and validates the resulting Go value against s. Number-precision-sensitive keywords see the original numeric text via json.Number.

func ValidateTOML

func ValidateTOML(s *Schema, data []byte, opts ...Option) (*Result, error)

ValidateTOML decodes data as TOML and validates the resulting Go value against s.

func ValidateYAML

func ValidateYAML(s *Schema, data []byte, opts ...Option) (*Result, error)

ValidateYAML decodes data as YAML and validates the resulting Go value against s.

Example

ExampleValidateYAML validates a YAML-encoded instance against a JSON schema using the streaming convenience entry point. The schema itself is supplied as JSON (LoadYAML covers schemas authored in YAML).

package main

import (
	"fmt"

	"github.com/go-rotini/jsonschema"
)

func main() {
	schema := jsonschema.MustCompile([]byte(`{"type":"integer","minimum":0}`))
	instanceYAML := []byte("42\n")
	res, err := jsonschema.ValidateYAML(schema, instanceYAML)
	if err != nil {
		fmt.Println("err:", err)
		return
	}
	fmt.Println(res.Valid)
}
Output:
true

func (*Result) Output

func (r *Result) Output(format OutputFormat) []byte

Output renders the result in the requested format per Draft 2020-12 §12. The four formats are:

  • OutputFlag: {"valid": true|false} only.
  • OutputBasic: a flat list of assertion outcomes with location info.
  • OutputDetailed: a nested tree of failures, collapsing passing groups.
  • OutputVerbose: the full nested tree, including passing groups.

The returned bytes are valid JSON and validate against the Draft 2020-12 output meta-schema (https://json-schema.org/draft/2020-12/output/schema).

Example

ExampleResult_Output renders a validation result in OutputFlag form.

package main

import (
	"fmt"

	"github.com/go-rotini/jsonschema"
)

func main() {
	schema := jsonschema.MustCompile([]byte(`{"type":"number"}`))
	res, _ := schema.Validate([]byte(`"oops"`))
	fmt.Println(string(res.Output(jsonschema.OutputFlag)))
}
Output:
{"valid":false}

type Schema

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

Schema is a compiled JSON Schema. Once produced by Compile (or its siblings), a Schema is immutable and safe for concurrent validation — callers may freely share a single *Schema across goroutines and call any of the Validate-family methods concurrently.

func Compile

func Compile(schemaJSON []byte, opts ...CompileOption) (*Schema, error)

Compile parses a JSON Schema document and returns a *Schema ready for validation.

The schema's effective draft is determined by the $schema keyword if present; otherwise the package falls back to the value passed via WithDefaultDraft, then DraftDefault.

Example

ExampleCompile shows the one-shot Compile entry point used to amortize schema compilation across many validations.

package main

import (
	"fmt"

	"github.com/go-rotini/jsonschema"
)

func main() {
	schema, err := jsonschema.Compile([]byte(`{"type":"string","minLength":3}`))
	if err != nil {
		fmt.Println("compile error:", err)
		return
	}
	res, err := schema.Validate([]byte(`"hello"`))
	if err != nil {
		fmt.Println("validate error:", err)
		return
	}
	fmt.Println(res.Valid)
}
Output:
true

func CompileURL

func CompileURL(uri string, opts ...CompileOption) (*Schema, error)

CompileURL fetches the schema at uri using the configured Loader (or the default chain) and compiles it.

func CompileValue

func CompileValue(schemaValue any, opts ...CompileOption) (*Schema, error)

CompileValue compiles a schema already represented as a Go value (the result of json.Unmarshal into any, or a jsonc/yaml/toml decode). Useful when the schema is constructed in code or arrives from a non-JSON source.

func FromType

func FromType(t reflect.Type, opts ...GenerateOption) (*Schema, error)

FromType is the type-only counterpart to Generate; useful when the caller has a reflect.Type but no value.

func Generate

func Generate(v any, opts ...GenerateOption) (*Schema, error)

Generate returns a *Schema describing the type of v. The walker honors `json` and `jsonschema` struct tags (see §6 of the requirements doc).

Generate accepts any Go value; only its runtime type is consulted, the value itself is not inspected. Pass a nil-typed value (e.g. `(*MyType)(nil)`) to describe a type without constructing one.

Example

ExampleGenerate emits a JSON Schema describing a Go struct.

package main

import (
	"fmt"

	"github.com/go-rotini/jsonschema"
)

func main() {
	type User struct {
		Name string `json:"name" jsonschema:"required,minLength=1"`
	}
	schema, err := jsonschema.Generate((*User)(nil))
	if err != nil {
		fmt.Println("err:", err)
		return
	}
	out, _ := schema.MarshalJSON()
	fmt.Println(string(out))
}
Output:
{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1}},"required":["name"]}

func LoadJSON

func LoadJSON(schemaJSON []byte, opts ...CompileOption) (*Schema, error)

LoadJSON parses a JSON schema document and compiles it. It is a verbatim alias for Compile provided for naming symmetry with LoadJSONC, LoadYAML, and LoadTOML when callers dispatch on a runtime format label.

func LoadJSONC

func LoadJSONC(schemaJSONC []byte, opts ...CompileOption) (*Schema, error)

LoadJSONC parses a JSONC schema document via github.com/go-rotini/jsonc and compiles it as a JSON Schema. Comments and trailing commas in the source are tolerated; numeric literals retain their original text via json.Number so multipleOf / minimum / maximum evaluate correctly.

Example

ExampleLoadJSONC compiles a schema authored as JSONC (JSON with comments) and validates a JSON instance against it.

package main

import (
	"fmt"

	"github.com/go-rotini/jsonschema"
)

func main() {
	schemaJSONC := []byte(`{
		// minimum string length is 3
		"type": "string",
		"minLength": 3
	}`)
	schema, err := jsonschema.LoadJSONC(schemaJSONC)
	if err != nil {
		fmt.Println("load error:", err)
		return
	}
	res, _ := schema.Validate([]byte(`"hello"`))
	fmt.Println(res.Valid)
}
Output:
true

func LoadJSONCURL

func LoadJSONCURL(uri string, opts ...CompileOption) (*Schema, error)

LoadJSONCURL fetches a JSONC schema document from uri using the configured loader and compiles it. Mirrors CompileURL for the JSONC adapter.

func LoadJSONCValue

func LoadJSONCValue(v any, opts ...CompileOption) (*Schema, error)

LoadJSONCValue compiles an already-decoded Go value as if it had been produced by the JSONC adapter. Provided for symmetry with CompileValue when the caller has run their own JSONC decoder.

func LoadJSONURL

func LoadJSONURL(uri string, opts ...CompileOption) (*Schema, error)

LoadJSONURL fetches a JSON schema document from uri using the configured loader and compiles it. Alias for CompileURL.

func LoadJSONValue

func LoadJSONValue(v any, opts ...CompileOption) (*Schema, error)

LoadJSONValue compiles an already-decoded Go value as a JSON schema. Alias for CompileValue.

func LoadTOML

func LoadTOML(schemaTOML []byte, opts ...CompileOption) (*Schema, error)

LoadTOML parses a TOML schema document and compiles it as a JSON Schema.

Example

ExampleLoadTOML compiles a schema authored as TOML and validates a JSON instance against it.

package main

import (
	"fmt"

	"github.com/go-rotini/jsonschema"
)

func main() {
	schemaTOML := []byte(`type = "string"
minLength = 1
`)
	schema, err := jsonschema.LoadTOML(schemaTOML)
	if err != nil {
		fmt.Println("load error:", err)
		return
	}
	res, _ := schema.Validate([]byte(`"hi"`))
	fmt.Println(res.Valid)
}
Output:
true

func LoadTOMLURL

func LoadTOMLURL(uri string, opts ...CompileOption) (*Schema, error)

LoadTOMLURL fetches a TOML schema document from uri using the configured loader and compiles it. Mirrors CompileURL for the TOML adapter.

func LoadTOMLValue

func LoadTOMLValue(v any, opts ...CompileOption) (*Schema, error)

LoadTOMLValue compiles an already-decoded Go value as if it had been produced by the TOML adapter. Provided for symmetry with CompileValue when the caller has run their own TOML decoder.

func LoadYAML

func LoadYAML(schemaYAML []byte, opts ...CompileOption) (*Schema, error)

LoadYAML parses a YAML schema document and compiles it as a JSON Schema. The first document in the stream is used; multi-document streams are rejected with ErrInvalidYAML.

Example

ExampleLoadYAML compiles a schema authored as YAML and validates a JSON instance against it.

package main

import (
	"fmt"

	"github.com/go-rotini/jsonschema"
)

func main() {
	schemaYAML := []byte(`type: integer
minimum: 0
maximum: 100
`)
	schema, err := jsonschema.LoadYAML(schemaYAML)
	if err != nil {
		fmt.Println("load error:", err)
		return
	}
	res, _ := schema.Validate([]byte(`42`))
	fmt.Println(res.Valid)
}
Output:
true

func LoadYAMLURL

func LoadYAMLURL(uri string, opts ...CompileOption) (*Schema, error)

LoadYAMLURL fetches a YAML schema document from uri using the configured loader and compiles it. Mirrors CompileURL for the YAML adapter.

func LoadYAMLValue

func LoadYAMLValue(v any, opts ...CompileOption) (*Schema, error)

LoadYAMLValue compiles an already-decoded Go value as if it had been produced by the YAML adapter. Provided for symmetry with CompileValue when the caller has run their own YAML decoder.

func MetaSchema

func MetaSchema(d Draft) (*Schema, error)

MetaSchema returns the compiled *Schema for the canonical meta-schema of d. The result is memoized; repeated calls return the same pointer.

The compile path uses a Compiler backed by the embedded meta-schema MapLoader so refs into the per-vocabulary meta-schemas resolve without network access.

func MustCompile

func MustCompile(schemaJSON []byte, opts ...CompileOption) *Schema

MustCompile is the panic-on-error variant of Compile. Intended for package-init use of static, well-known schemas.

func MustCompileURL

func MustCompileURL(uri string, opts ...CompileOption) *Schema

MustCompileURL is the panic-on-error variant of CompileURL.

func MustCompileValue

func MustCompileValue(schemaValue any, opts ...CompileOption) *Schema

MustCompileValue is the panic-on-error variant of CompileValue.

func MustGenerate

func MustGenerate(v any, opts ...GenerateOption) *Schema

MustGenerate is the panic-on-error variant of Generate. Intended for package-init use of static, well-known types.

func MustLoadJSON

func MustLoadJSON(schemaJSON []byte, opts ...CompileOption) *Schema

MustLoadJSON is the panic-on-error variant of LoadJSON.

func MustLoadJSONC

func MustLoadJSONC(schemaJSONC []byte, opts ...CompileOption) *Schema

MustLoadJSONC is the panic-on-error variant of LoadJSONC.

func MustLoadJSONCURL

func MustLoadJSONCURL(uri string, opts ...CompileOption) *Schema

MustLoadJSONCURL is the panic-on-error variant of LoadJSONCURL.

func MustLoadJSONCValue

func MustLoadJSONCValue(v any, opts ...CompileOption) *Schema

MustLoadJSONCValue is the panic-on-error variant of LoadJSONCValue.

func MustLoadJSONURL

func MustLoadJSONURL(uri string, opts ...CompileOption) *Schema

MustLoadJSONURL is the panic-on-error variant of LoadJSONURL.

func MustLoadJSONValue

func MustLoadJSONValue(v any, opts ...CompileOption) *Schema

MustLoadJSONValue is the panic-on-error variant of LoadJSONValue.

func MustLoadTOML

func MustLoadTOML(schemaTOML []byte, opts ...CompileOption) *Schema

MustLoadTOML is the panic-on-error variant of LoadTOML.

func MustLoadTOMLURL

func MustLoadTOMLURL(uri string, opts ...CompileOption) *Schema

MustLoadTOMLURL is the panic-on-error variant of LoadTOMLURL.

func MustLoadTOMLValue

func MustLoadTOMLValue(v any, opts ...CompileOption) *Schema

MustLoadTOMLValue is the panic-on-error variant of LoadTOMLValue.

func MustLoadYAML

func MustLoadYAML(schemaYAML []byte, opts ...CompileOption) *Schema

MustLoadYAML is the panic-on-error variant of LoadYAML.

func MustLoadYAMLURL

func MustLoadYAMLURL(uri string, opts ...CompileOption) *Schema

MustLoadYAMLURL is the panic-on-error variant of LoadYAMLURL.

func MustLoadYAMLValue

func MustLoadYAMLValue(v any, opts ...CompileOption) *Schema

MustLoadYAMLValue is the panic-on-error variant of LoadYAMLValue.

func OutputMetaSchema

func OutputMetaSchema() *Schema

OutputMetaSchema returns the compiled Draft 2020-12 output-format meta-schema (memoized; embedded). It returns nil only when the embedded bytes fail to compile, which would indicate a build-time mistake.

func (*Schema) Anchors

func (s *Schema) Anchors() []string

Anchors returns the plain-name anchors declared in the root resource. Returns nil when the schema is nil or was constructed without a resource map. The returned slice is freshly allocated; callers may mutate it.

func (*Schema) Bindings

func (s *Schema) Bindings() []KeywordBinding

Bindings returns the keyword bindings extracted at compile time. Each binding records the keyword name, its source location, and the keyword's raw value. Returns a fresh slice each call; the slice is safe for callers to mutate, but the embedded RawValue may share storage with the schema's parsed source.

In v0.1, ref-resolution targets are not exposed in the public binding — only Name, Location, and RawValue are populated. v0.2 may extend KeywordBinding with a typed Resolved field.

func (*Schema) Draft

func (s *Schema) Draft() Draft

Draft returns the draft this schema was compiled against.

func (*Schema) ID

func (s *Schema) ID() string

ID returns the schema's $id (the absolute URI); empty if no $id was declared at the schema root.

func (*Schema) MarshalJSON

func (s *Schema) MarshalJSON() ([]byte, error)

MarshalJSON returns the schema's source bytes. A *Schema therefore embeds transparently in encoding/json.Marshal output.

func (*Schema) MetaSchemaURI

func (s *Schema) MetaSchemaURI() string

MetaSchemaURI returns the meta-schema URI declared via the $schema keyword, or the canonical URL of the schema's draft when $schema was absent.

func (*Schema) Resources

func (s *Schema) Resources() []string

Resources returns the absolute URIs of every $id-bounded resource the *Schema directly contains: the root URI first, then each nested resource in declaration order. Returns nil when the schema is nil or was constructed without a resource map.

func (*Schema) String

func (s *Schema) String() string

String returns a brief, human-readable summary of the schema for log lines and debug output. Format: `Schema(<draft> [id=<id>] [bytes=<n>])`.

func (*Schema) Validate

func (s *Schema) Validate(instanceJSON []byte, opts ...Option) (*Result, error)

Validate validates instanceJSON against the schema and returns a *Result.

Empty input ([]byte{} or all-whitespace) is rejected with an error wrapping io.EOF. The literal `null` is a valid JSON value and validates against any schema that accepts the null type; it is never treated as "no input".

Example

ExampleSchema_Validate demonstrates validating an instance against a previously compiled schema and inspecting the failure list.

package main

import (
	"fmt"

	"github.com/go-rotini/jsonschema"
)

func main() {
	schema := jsonschema.MustCompile([]byte(`{
		"type":"object",
		"properties":{"name":{"type":"string"}},
		"required":["name"]
	}`))
	res, _ := schema.Validate([]byte(`{}`))
	fmt.Println(res.Valid, len(res.Errors) > 0)
}
Output:
false true
Example (Integer)

ExampleSchema_Validate_integer demonstrates compiling a schema with a numeric type assertion and validating an integer instance.

package main

import (
	"fmt"

	"github.com/go-rotini/jsonschema"
)

func main() {
	schema := jsonschema.MustCompile([]byte(`{"type":"integer"}`))
	res, err := schema.Validate([]byte(`42`))
	if err != nil {
		fmt.Println("err:", err)
		return
	}
	fmt.Println(res.Valid)
}
Output:
true

func (*Schema) ValidateAndUnmarshal

func (s *Schema) ValidateAndUnmarshal(instanceJSON []byte, v any, opts ...Option) error

ValidateAndUnmarshal validates instanceJSON, then (on success) decodes it into v.

func (*Schema) ValidateReader

func (s *Schema) ValidateReader(r io.Reader, opts ...Option) (*Result, error)

ValidateReader streams instance bytes from r and validates them against the schema.

func (*Schema) ValidateValue

func (s *Schema) ValidateValue(v any, opts ...Option) (*Result, error)

ValidateValue validates an already-decoded Go value against the schema.

func (*Schema) Vocabularies

func (s *Schema) Vocabularies() []string

Vocabularies returns the set of vocabulary URIs declared by the schema's effective $vocabulary keyword (or, when absent, the standard set for the schema's draft). Returns a fresh slice each call; safe for callers to mutate.

In v0.1, $vocabulary at the schema root is recognized as a structural keyword but not honored as an opt-in selector — the returned set is always the standard vocabularies for Schema.Draft. When the root schema declares a VocabOAS-aware $schema (the OASDialectURL), VocabOAS is included alongside the draft's standard set. Custom vocabulary registration is reserved for v0.2.

type UnknownFormatPolicy

type UnknownFormatPolicy int

UnknownFormatPolicy controls how the validator reacts to an unrecognized "format" keyword value when format assertion is enabled.

const (
	// UnknownFormatIgnore silently accepts unknown formats. This is the
	// spec default behavior and matches most existing implementations.
	UnknownFormatIgnore UnknownFormatPolicy = iota
	// UnknownFormatWarn records an annotation noting the unknown format
	// but does not fail validation.
	UnknownFormatWarn
	// UnknownFormatError fails validation when an unknown format is
	// encountered.
	UnknownFormatError
)

Recognized unknown-format policies.

func (UnknownFormatPolicy) String

func (p UnknownFormatPolicy) String() string

String returns a human-readable label for p.

type ValidationError

type ValidationError struct {
	// KeywordLocation is the JSON Pointer to the failing keyword in the
	// schema (e.g. "/properties/name/minLength").
	KeywordLocation string
	// AbsoluteKeywordLocation is the resolved-URL form of KeywordLocation;
	// empty unless the schema was loaded from a remote URI.
	AbsoluteKeywordLocation string
	// InstanceLocation is the JSON Pointer to the failing value in the
	// instance (e.g. "/name").
	InstanceLocation string
	// Keyword is the bare keyword name that triggered the failure
	// (e.g. "minLength"). This is the stable, machine-readable
	// classification of the error.
	Keyword string
	// Message is a human-readable description of the failure.
	Message string
	// Causes carries nested failures from compound applicators; it is empty
	// for leaf assertion failures.
	Causes []ValidationError
	// Cause carries an optional underlying typed error (e.g. a
	// [*FormatError] surfaced by the format assertion). Walked by
	// [errors.As] via [ValidationError.Unwrap].
	Cause error
}

ValidationError represents a single assertion failure surfaced by the validator. Failures from compound applicators (anyOf, oneOf, $ref, ...) expose their nested causes via [ValidationError.Causes]; the Go 1.20 multi- error errors.Unwrap convention is honored so that errors.Is / errors.As can walk the cause chain.

func (*ValidationError) Error

func (e *ValidationError) Error() string

Error returns a human-readable, single-line summary of the failure suitable for log lines. Programmatic callers should switch on [ValidationError.Keyword] rather than parse the message text.

func (*ValidationError) Is

func (e *ValidationError) Is(target error) bool

Is reports whether target is the ErrValidation sentinel. Only the zero-value *ValidationError (i.e. ErrValidation) matches; another concrete *ValidationError does not match unrelated keyword failures via errors.Is. Use errors.As to extract the typed error and inspect [ValidationError.Keyword] for stable classification.

func (*ValidationError) Unwrap

func (e *ValidationError) Unwrap() []error

Unwrap returns the nested causes for use with Go 1.20+ multi-error errors.Is / errors.As. Returns nil when there are no causes so that the stdlib treats the error as a leaf. When [ValidationError.Cause] is set it is included alongside any nested [Causes].

type Vocabulary

type Vocabulary struct {
	// URI is the canonical identifier for the vocabulary (e.g.
	// VocabApplicator).
	URI string
	// Keywords lists every keyword that belongs to the vocabulary. The
	// order is informational; the package does not depend on it.
	Keywords []Keyword
}

Vocabulary groups a set of keywords under a single URI per Draft 2019-09's vocabulary mechanism. The standard vocabularies are registered at package init time; custom-vocabulary registration is reserved for v0.2.

func Vocabularies

func Vocabularies() []Vocabulary

Vocabularies returns the registered standard vocabularies in declaration order. The returned slice is a defensive copy; the underlying Keyword values are immutable so the per-element copy does not deep-copy them.

Directories

Path Synopsis
Command bowtie is the stdin/stdout adapter that exposes github.com/go-rotini/jsonschema to the Bowtie cross-implementation conformance harness (https://bowtie.report).
Command bowtie is the stdin/stdout adapter that exposes github.com/go-rotini/jsonschema to the Bowtie cross-implementation conformance harness (https://bowtie.report).

Jump to

Keyboard shortcuts

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