NAME
JSON::Schema::Validate - Lean, recursion-safe JSON Schema validator (Draft 2020-12)
SYNOPSIS
use JSON::Schema::Validate;
use JSON ();
my $schema = {
'$schema' => 'https://json-schema.org/draft/2020-12/schema',
'$id' => 'https://example.org/s/root.json',
type => 'object',
required => [ 'name' ],
properties => {
name => { type => 'string', minLength => 1 },
next => { '$dynamicRef' => '#Node' },
},
'$dynamicAnchor' => 'Node',
additionalProperties => JSON::false,
};
my $js = JSON::Schema::Validate->new( $schema )
->register_builtin_formats;
my $ok = $js->validate({ name => 'head', next=>{ name => 'tail' } })
or die $js->error;
print "ok\n";
VERSION
v0.1.0
DESCRIPTION
JSON::Schema::Validate is a compact, dependency-light validator for JSON Schema draft 2020-12. It focuses on:
Correctness and recursion safety (supports
$ref,$dynamicRef,$anchor,$dynamicAnchor).Draft 2020-12 evaluation semantics, including
unevaluatedItemsandunevaluatedPropertieswith annotation tracking.A practical Perl API (constructor takes the schema; call
validatewith your data; inspecterror/errorson failure).Builtin validators for common
formats (date, time, email, hostname, ip, uri, uuid, JSON Pointer, etc.), with the option to register or override custom format handlers.
This module is intentionally minimal compared to large reference implementations, but it implements the parts most people rely on in production.
Supported Keywords (2020-12)
Types
type(string or array of strings), including union types. Unions may also include inline schemas (e.g.type => [ 'integer', { minimum => 0 } ]).Constant / Enumerations
const,enum.Numbers
multipleOf,minimum,maximum,exclusiveMinimum,exclusiveMaximum.Strings
minLength,maxLength,pattern,format.Arrays
prefixItems,items,contains,minContains,maxContains,uniqueItems,unevaluatedItems.Objects
properties,patternProperties,additionalProperties,propertyNames,required,dependentRequired,dependentSchemas,unevaluatedProperties.Combinators
allOf,anyOf,oneOf,not.Conditionals
if,then,else.Referencing
$id,$anchor,$ref,$dynamicAnchor,$dynamicRef.
Formats
Call register_builtin_formats to install default validators for the following format names:
date-time,date,time,durationLeverages DateTime and DateTime::Format::ISO8601 when available (falls back to strict regex checks). Duration uses DateTime::Duration.
email,idn-emailUses Regexp::Common with
Email::Addressif available.hostname,idn-hostnameidn-hostnameuses Net::IDN::Encode if available; otherwise, applies a permissive Unicode label check and thenhostnamerules.ipv4,ipv6Strict regex-based validation.
uri,uri-reference,iriReasonable regex checks for scheme and reference forms (not a full RFC parser).
uuidHyphenated 8-4-4-4-12 hex.
json-pointer,relative-json-pointerConformant to RFC 6901 and the relative variant used by JSON Schema.
regexChecks that the pattern compiles in Perl.
Custom formats can be registered or override builtins via register_format or the format => { ... } constructor option (see "METHODS").
METHODS
new
my $js = JSON::Schema::Validate->new( $schema, %opts );
Build a validator from a decoded JSON Schema (Perl hash/array structure).
Options (all optional):
format => \%callbacks-
Hash of
format_name => sub { ... }validators. Each sub receives the string to validate and must return true/false. Entries here take precedence when you later callregister_builtin_formats(i.e. your callbacks remain in place). fnormalize_instance => 1|0-
Defaults to
1When true, the instance is round-tripped through JSON before validation, which enforces strict JSON typing (strings remain strings; numbers remain numbers). This matches Python
jsonschema’s type behaviour. Set to0if you prefer Perl’s permissive numeric/string duality.
register_builtin_formats
$js->register_builtin_formats;
Registers the built-in validators listed in "Formats". Existing user-supplied format callbacks are preserved if they already exist under the same name.
register_format
$js->register_format( $name, sub { ... } );
Register or override a format validator at runtime. The sub receives a single scalar (the candidate string) and must return true/false.
set_resolver
$js->set_resolver( sub { my( $absolute_uri ) = @_; ...; return $schema_hashref } );
Install a resolver for external documents. It is called with an absolute URI (formed from the current base $id and the $ref) and must return a Perl hash reference representation of a JSON Schema. If the returned hash contains '$id', it will become the new base for that document; otherwise, the absolute URI is used as its base.
validate
my $ok = $js->validate( $data );
Validate a decoded JSON instance against the compiled schema. Returns a boolean. On failure, inspect $js->error for a concise message (first error), or $js->errors for an arrayref of hashes like:
{ path => '#/properties~1name', msg => 'string shorter than minLength 1' }
error
my $msg = $js->error;
Short, human-oriented message for the first failure.
errors
my $arrayref = $js->errors;
All collected errors (up to the internal max_errors cap).
BEHAVIOUR NOTES
Recursion & Cycles
The validator guards on the pair
(schema_pointer, instance_address), so self-referential schemas and cyclic instance graphs won’t infinite-loop.Union Types with Inline Schemas
typemay be an array mixing string type names and inline schemas. Any inline schema that validates the instance makes thetypecheck succeed.Booleans
For practicality in Perl,
type => 'boolean'accepts JSON-like booleans (e.g. true/false, 1/0 as strings) as well as Perl boolean objects (if you use a boolean class). If you need stricter behaviour, you can adapt_match_typeor introduce a constructor flag and branch there.Unevaluated*
Both
unevaluatedItemsandunevaluatedPropertiesare enforced using annotation produced by earlier keyword evaluations within the same schema object, matching draft 2020-12 semantics.Unsupported/Not Implemented
This module intentionally omits some rarely used 2020-12 control keywords such as
$vocabularyand$commentprocessing, and media-related keywords likecontentEncoding/contentMediaType. These can be added later if required.
CREDITS
Albert from OpenAI for his invaluable help.
AUTHOR
Jacques Deguest <jack@deguest.jp>
SEE ALSO
perl, DateTime, DateTime::Format::ISO8601, DateTime::Duration, Regexp::Common, Net::IDN::Encode, JSON::PP
COPYRIGHT & LICENSE
Copyright(c) 2025 DEGUEST Pte. Ltd.
All rights reserved.
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.