NAME

JSON::Schema::Corvus - Fast JSON Schema validation (draft 4 to 2020-12) in pure Perl

SYNOPSIS

use JSON::Schema::Corvus;

my $validator = JSON::Schema::Corvus->compile(<<'JSON');
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": { "id": { "type": "integer", "minimum": 1 } },
  "required": ["id"]
}
JSON

$validator->is_valid_json('{"id": 3}');   # true
$validator->is_valid_json('{"id": 0}');   # false
$validator->is_valid({ id => 3 });        # Perl data: true

my $collector = JSON::Schema::Corvus::ResultsCollector->new('detailed');
$validator->evaluate_json('{"id": "3"}', $collector);
for my $row (@{ $collector->results }) {
  print "$row->{document_evaluation_location}: $row->{message}\n" unless $row->{is_match};
}

DESCRIPTION

A JSON Schema validator for draft 4, 6, 7, 2019-09 and 2020-12, which passes the whole of the JSON-Schema-Test-Suite, with results and annotations. It is pure Perl, with no dependency beyond Perl's core: a port of the Corvus.Text.Json evaluator by way of its Python port. A schema is compiled into Perl, which validates with exactly the checks the schema needs. JSON::Schema::Corvus::XS is the same evaluator on the corvus-json-schema C library, with the same API, and is faster still.

JSON text is parsed by the package's own parser, which keeps numbers exactly as the text says: an integer of any size is an integer, and a number with a fraction or an exponent is a floating point number. A whole number is an integer in every draft, 1.0 included, as it is in the XS package. Parsing is in Perl, so for an instance validated more than once, parse it once into a "Documents" document.

Perl data is accepted too, by the rules JSON::PP follows: a scalar that has a number and has never been used as a string is a number, and any other defined scalar is a string; undef is null; JSON::PP::Boolean objects, Perl's own booleans (5.36 and later), \1 and \0 are booleans; a Math::BigInt is an integer, exactly; a Math::BigFloat is an integer when it is whole and otherwise the nearest floating point number; an object with a TO_JSON method is what that returns. The data is copied before it is validated.

A string of JSON text is read as UTF-8: a string with Perl's UTF8 flag on gives its characters, and one without gives its bytes.

Regular expressions

pattern, patternProperties and format: regex are ECMA-262 regular expressions, run with ECMA-262's meaning (Unicode 17, whatever version of Unicode the Perl has). A pattern is read with the grammar of the u flag, and one that only that grammar refuses with the Annex B grammar of a pattern with no flag. Perl's own engine runs a pattern that is proved to match in time proportional to the text, written out so that none of Perl's own meanings applies; every other pattern (a backreference, a lookbehind, or anything the proof does not prove) runs on the package's own matcher, with the limit of a match (pattern_match_limit). That matcher is in Perl, so a deliberately difficult pattern can reach the limit of one second where the XS package answers: raise the limit, or turn it off, for such patterns from a source you trust.

METHODS

compile

my $validator = JSON::Schema::Corvus->compile($schema, %options);

Compiles a schema, given as JSON text or as Perl data (a reference, or a boolean). The options:

default_dialect

The dialect of a schema without $schema: draft4, draft6, draft7, draft2019-09 or draft2020-12 (the default).

assert_format

True asserts format, false never does; left out (or undef), it follows the vocabularies (the 2020-12 format-assertion vocabulary asserts, everything else annotates).

assert_format_in_legacy_drafts

With assert_format left out, assert format in drafts 4 to 7 too.

assert_content

Assert contentEncoding and contentMediaType in draft 7, the only draft that asserts them (default true).

formats

Custom format assertions, by name: { even => sub { length($_[0]) % 2 == 0 } }. Each is called with the string (for a number, its JSON text) and returns whether it is valid. One takes precedence over the built-in format of its name.

resolve_document

sub ($uri) for documents a $ref names: it returns the document as JSON text or as Perl data, or undef for a document it does not know. The standard metaschemas are built in.

base_uri

The base URI of the root document.

entry_point

A reference, relative to the root, to evaluate from, such as #/$defs/item.

max_depth

The depth of in-place recursion on a cycle at which evaluation is abandoned (default 128).

pattern_match_limit

The seconds one match of a pattern or patternProperties regular expression against one string may take (default 1). undef or 0 turns the limit off, for a caller who trusts both the schema and the instances.

An exception a custom format or the resolver throws goes up through the call that ran it.

compile_uri

my $validator = JSON::Schema::Corvus->compile_uri($uri, %options);

Compiles the schema at an absolute URI, fetched through resolve_document (or one of the standard metaschemas).

Validators

is_valid_json($text)

Whether the JSON text is valid.

is_valid($data)

Whether the Perl data is valid.

is_valid_document($document)

Whether a "Documents" document is valid.

evaluate_json($text, $collector), evaluate($data, $collector), evaluate_document($document, $collector)

Evaluates every keyword, replacing the collector's results with this evaluation's, and returns whether the instance is valid.

source

The Perl the schema was compiled into.

A validator may be used any number of times, and in more than one thread.

Documents

my $document = JSON::Schema::Corvus::Document->parse($text);
my $document = JSON::Schema::Corvus::Document->from_data($data);

JSON parsed once, for an instance validated more than once.

Results

my $collector = JSON::Schema::Corvus::ResultsCollector->new($level);

A results collector at the basic level (failures, without messages), detailed (failures, with messages, the default) or verbose (every evaluation, passing and failing, with annotations). Its rows are those of the other Corvus evaluators: the same locations, messages and order. Its methods:

results

The rows of the last evaluation, as an array of hashes with is_match, message, evaluation_location (the keywords from the root schema), schema_evaluation_location (the schema or keyword within its document) and document_evaluation_location (the JSON pointer of the evaluated value).

annotations, annotations_json

The annotations of a verbose evaluation, grouped by instance location, keyword and schema location: { "/name" => { title => { "#/properties/name" => "Name" } } }, as Perl data or as JSON text.

count, clear

The number of rows, and removing them.

Other functions

JSON::Schema::Corvus::encode_json($data) returns the JSON text (UTF-8 bytes) of the JSON value that Perl data is validated as.

ERRORS

Failures are thrown as objects of these classes, which stringify to their message:

JSON::Schema::Corvus::Error

The base class, with message. Thrown itself for a bad argument or option.

JSON::Schema::Corvus::Error::InvalidJson

Text that is not JSON, or not UTF-8. offset is the byte offset at which it was found to be invalid.

JSON::Schema::Corvus::Error::Compilation

A schema that does not compile: a reference that does not resolve, or a regular expression that is not valid.

JSON::Schema::Corvus::Error::PatternTooLarge

A Compilation error for a regular expression over a limit of size: 1,048,576 bytes of pattern, 1,000 levels of groups, or 1,048,576 bytes counted for it as it is read.

JSON::Schema::Corvus::Error::Depth

Evaluation recursed in place beyond max_depth.

JSON::Schema::Corvus::Error::PatternMatchLimit

A match of a regular expression reached pattern_match_limit: the instance is neither valid nor not valid. It has the pattern and the limit.

Perl data that has no JSON form (a code reference, an object with no TO_JSON, a number that is not finite, or a structure nested more than 512 deep) dies with a message.

SEE ALSO

JSON::Schema::Corvus::XS, https://github.com/corvus-dotnet/Corvus.JsonSchema.

LICENSE

Apache License 2.0. The package holds tables derived from the Unicode Character Database, whose notice is in THIRD-PARTY-NOTICES.txt.