File Coverage

blib/lib/JSON/Validator.pm
Criterion Covered Total %
statement 545 561 97.1
branch 344 376 91.4
condition 162 190 85.2
subroutine 63 64 98.4
pod 10 10 100.0
total 1124 1201 93.5


line stmt bran cond sub pod time code
1             package JSON::Validator;
2 49     49   9357610 use Mojo::Base -base;
  49         527  
  49         300  
3              
4 49     49   7148 use B;
  49         105  
  49         2196  
5 49     49   272 use Carp 'confess';
  49         80  
  49         2206  
6 49     49   266 use Exporter 'import';
  49         121  
  49         1311  
7 49     49   19250 use JSON::Validator::Error;
  49         126  
  49         336  
8 49     49   21447 use JSON::Validator::Formats;
  49         132  
  49         1763  
9 49     49   21098 use JSON::Validator::Joi;
  49         225  
  49         474  
10 49     49   24841 use JSON::Validator::Ref;
  49         147  
  49         1818  
11             use JSON::Validator::Util
12 49     49   355 qw(E data_checksum data_section data_type is_type json_pointer prefix_errors schema_type uniq);
  49         110  
  49         3618  
13 49     49   313 use Mojo::File 'path';
  49         100  
  49         1948  
14 49     49   21648 use Mojo::JSON::Pointer;
  49         31172  
  49         408  
15 49     49   1907 use Mojo::JSON qw(false true);
  49         117  
  49         2354  
16 49     49   21823 use Mojo::URL;
  49         354431  
  49         385  
17 49     49   2251 use Mojo::Util qw(url_unescape sha1_sum);
  49         121  
  49         2487  
18 49     49   321 use Scalar::Util qw(blessed refaddr);
  49         121  
  49         2789  
19              
20 49     49   302 use constant CASE_TOLERANT => File::Spec->case_tolerant;
  49         106  
  49         4140  
21 49   50 49   310 use constant DEBUG => $ENV{JSON_VALIDATOR_DEBUG} || 0;
  49         117  
  49         3244  
22 49   50 49   304 use constant RECURSION_LIMIT => $ENV{JSON_VALIDATOR_RECURSION_LIMIT} || 100;
  49         106  
  49         3036  
23 49     49   322 use constant SPECIFICATION_URL => 'http://json-schema.org/draft-04/schema#';
  49         130  
  49         2955  
24 49     49   334 use constant YAML_SUPPORT => eval 'use YAML::XS 0.67;1';
  49     49   139  
  49         4343  
  49         154774  
  0         0  
  0         0  
25              
26             our $VERSION = '3.22';
27             our @EXPORT_OK = qw(joi validate_json);
28              
29             my $BUNDLED_CACHE_DIR = path(path(__FILE__)->dirname, qw(Validator cache));
30             my $HTTP_SCHEME_RE = qr{^https?:};
31              
32             has cache_paths => sub {
33             return [split(/:/, $ENV{JSON_VALIDATOR_CACHE_PATH} || ''),
34             $BUNDLED_CACHE_DIR];
35             };
36              
37             has formats => sub { shift->_build_formats };
38              
39             has generate_definitions_path => sub {
40             my $self = shift;
41             Scalar::Util::weaken($self);
42             return sub { [$self->{definitions_key} || 'definitions'] };
43             };
44              
45             has version => 4;
46              
47             has ua => sub {
48             require Mojo::UserAgent;
49             my $ua = Mojo::UserAgent->new;
50             $ua->proxy->detect;
51             $ua->max_redirects(3);
52             $ua;
53             };
54              
55             sub bundle {
56 15     15 1 8933 my ($self, $args) = @_;
57 15         23 my ($cloner);
58              
59             my $schema
60 15 100       47 = $args->{schema} ? $self->_resolve($args->{schema}) : $self->schema->data;
61 15         78 my @topics = ([$schema, my $bundle = {}, '']); # ([$from, $to], ...);
62              
63 15 100       37 if ($args->{replace}) {
64             $cloner = sub {
65 15     15   21 my $from = shift;
66 15         24 my $from_type = ref $from;
67 15         21 my $tied;
68 15 100 100     52 $from = $tied->schema if $from_type eq 'HASH' and $tied = tied %$from;
69 15 100       46 my $to = $from_type eq 'ARRAY' ? [] : $from_type eq 'HASH' ? {} : $from;
    50          
70 15 100       35 push @topics, [$from, $to] if $from_type;
71 15         47 return $to;
72 3         15 };
73             }
74             else {
75             $cloner = sub {
76 178     178   262 my $from = shift;
77 178         256 my $from_type = ref $from;
78              
79 178   100     404 my $tied = $from_type eq 'HASH' && tied %$from;
80 178 100       305 unless ($tied) {
81 143 100       289 my $to = $from_type eq 'ARRAY' ? [] : $from_type eq 'HASH' ? {} : $from;
    50          
82 143 100       278 push @topics, [$from, $to] if $from_type;
83 143         446 return $to;
84             }
85              
86             return $from
87             if !$args->{schema}
88 35 100 100     117 and $tied->fqn =~ m!^\Q$self->{root_schema_url}\E\#!;
89              
90 27         287 my $path = $self->_definitions_path($bundle, $tied);
91 27 50       104 unless ($self->{bundled_refs}{$tied->fqn}++) {
92 27   100     62 push @topics,
93             [_node($schema, $path, 1, 0) || {}, _node($bundle, $path, 1, 1)];
94 27         74 push @topics, [$tied->schema, _node($bundle, $path, 0, 1)];
95             }
96              
97 27         83 $path = join '/', '#', @$path;
98 27         80 tie my %ref, 'JSON::Validator::Ref', $tied->schema, $path;
99 27         141 return \%ref;
100 12         61 };
101             }
102              
103             Mojo::Util::deprecated('bundle({ref_key => "..."}) will be removed.')
104 15 50       40 if $args->{ref_key};
105 15         36 local $self->{definitions_key} = $args->{ref_key};
106 15         30 local $self->{bundled_refs} = {};
107              
108 15         34 while (@topics) {
109 130         182 my ($from, $to) = @{shift @topics};
  130         242  
110 130 50       353 if (ref $from eq 'ARRAY') {
    50          
111 0         0 for (my $i = 0; $i < @$from; $i++) {
112 0         0 $to->[$i] = $cloner->($from->[$i]);
113             }
114             }
115             elsif (ref $from eq 'HASH') {
116 130         336 for my $key (keys %$from) {
117 208   100     673 $to->{$key} //= $cloner->($from->{$key});
118             }
119             }
120             }
121              
122 15         213 return $bundle;
123             }
124              
125             sub coerce {
126 19     19 1 586 my $self = shift;
127 19 100 50     90 return $self->{coerce} ||= {} unless defined(my $what = shift);
128              
129 14 100       53 if ($what eq '1') {
130 1         5 Mojo::Util::deprecated('coerce(1) will be deprecated.');
131 1         427 $what = {booleans => 1, numbers => 1, strings => 1};
132             }
133              
134 14         71 state $short = {bool => 'booleans', def => 'defaults', num => 'numbers',
135             str => 'strings'};
136              
137 14 100       76 $what = {map { ($_ => 1) } split /,/, $what} unless ref $what;
  10         58  
138 14         49 $self->{coerce} = {};
139 14   66     126 $self->{coerce}{($short->{$_} || $_)} = $what->{$_} for keys %$what;
140              
141 14         100 return $self;
142             }
143              
144 10     10 1 2097 sub get { JSON::Validator::Util::schema_extract(shift->schema->data, shift) }
145              
146             sub joi {
147 52 100   52 1 30152 return JSON::Validator::Joi->new unless @_;
148 2         5 my ($data, $joi) = @_;
149 2         7 return $joi->validate($data, $joi);
150             }
151              
152             sub load_and_validate_schema {
153 270     270 1 31745 my ($self, $spec, $args) = @_;
154 270   100     1328 my $schema = $args->{schema} || SPECIFICATION_URL;
155 270 100 66     2700 $self->version($1) if !$self->{version} and $schema =~ /draft-0+(\w+)/;
156 270         2253 $spec = $self->_resolve($spec);
157 265         1141 my @errors = $self->new(%$self)->schema($schema)->validate($spec);
158 265 100       1607 confess join "\n", "Invalid JSON specification $spec:", map {"- $_"} @errors
  1         5  
159             if @errors;
160 264         975 $self->{schema} = Mojo::JSON::Pointer->new($spec);
161 264         4023 $self;
162             }
163              
164             sub new {
165 581     581 1 423276 my $self = shift->SUPER::new(@_);
166 581 100       5197 $self->coerce($self->{coerce}) if defined $self->{coerce};
167 581         1980 return $self;
168             }
169              
170             sub schema {
171 1554     1554 1 99984 my $self = shift;
172 1554 100       4769 return $self->{schema} unless @_;
173 636         1675 $self->{schema} = Mojo::JSON::Pointer->new($self->_resolve(shift));
174 633         7818 return $self;
175             }
176              
177 9     9 1 36 sub singleton { state $jv = shift->new }
178              
179             sub validate {
180 929     929 1 36707 my ($self, $data, $schema) = @_;
181 929   100     3380 $schema ||= $self->schema->data;
182 929 50       5853 return E '/', 'No validation rules defined.' unless defined $schema;
183              
184 929         2252 local $self->{schema} = Mojo::JSON::Pointer->new($schema);
185 929         9176 local $self->{seen} = {};
186 929         2434 local $self->{temp_schema} = []; # make sure random-errors.t does not fail
187 929         2604 my @errors = $self->_validate($_[1], '', $schema);
188 929         6991 return @errors;
189             }
190              
191             sub validate_json {
192 9     9 1 63158 __PACKAGE__->singleton->schema($_[1])->validate($_[0]);
193             }
194              
195             sub _build_formats {
196             return {
197 17     17   1162 'date' => JSON::Validator::Formats->can('check_date'),
198             'date-time' => JSON::Validator::Formats->can('check_date_time'),
199             'email' => JSON::Validator::Formats->can('check_email'),
200             'hostname' => JSON::Validator::Formats->can('check_hostname'),
201             'idn-email' => JSON::Validator::Formats->can('check_idn_email'),
202             'idn-hostname' => JSON::Validator::Formats->can('check_idn_hostname'),
203             'ipv4' => JSON::Validator::Formats->can('check_ipv4'),
204             'ipv6' => JSON::Validator::Formats->can('check_ipv6'),
205             'iri' => JSON::Validator::Formats->can('check_iri'),
206             'iri-reference' => JSON::Validator::Formats->can('check_iri_reference'),
207             'json-pointer' => JSON::Validator::Formats->can('check_json_pointer'),
208             'regex' => JSON::Validator::Formats->can('check_regex'),
209             'relative-json-pointer' =>
210             JSON::Validator::Formats->can('check_relative_json_pointer'),
211             'time' => JSON::Validator::Formats->can('check_time'),
212             'uri' => JSON::Validator::Formats->can('check_uri'),
213             'uri-reference' => JSON::Validator::Formats->can('check_uri_reference'),
214             'uri-reference' => JSON::Validator::Formats->can('check_uri_reference'),
215             'uri-template' => JSON::Validator::Formats->can('check_uri_template'),
216             };
217             }
218              
219             sub _definitions_path {
220 27     27   61 my ($self, $bundle, $ref) = @_;
221 27         76 my $path = $self->generate_definitions_path->($ref);
222              
223             # No need to rewrite, if it already has a nice name
224 27         79 my $node = _node($bundle, $path, 2, 0);
225 27         58 my $prefix = join '/', @$path;
226 27 100       71 if ($ref->fqn =~ m!#/$prefix/([^/]+)$!) {
227 4         15 my $key = $1;
228              
229 4 50 66     11 if ( $self->{bundled_refs}{$ref->fqn}
      66        
      33        
230             or !$node
231             or !$node->{$key}
232             or data_checksum($ref->schema) eq data_checksum($node->{$key}))
233             {
234 4         19 return [@$path, $key];
235             }
236             }
237              
238             # Generate definitions key based on filename
239 23         62 my ($spec_path, $fragment) = split '#', $ref->fqn;
240 23         45 my $key = $fragment;
241 23 50       445 if (-e $spec_path) {
242 23         102 $key = join '-', map { s!^\W+!!; $_ } grep {$_} path($spec_path)->basename,
  51         116  
  51         131  
  69         1461  
243             $fragment, substr(sha1_sum($spec_path), 0, 10);
244             }
245              
246             # Fallback or nicer path name
247 23         122 $key =~ s![^\w-]!_!g;
248 23         93 return [@$path, $key];
249             }
250              
251             # Try not to break JSON::Validator::OpenAPI::Mojolicious
252 0     0   0 sub _get { shift; JSON::Validator::Util::_schema_extract(@_) }
  0         0  
253              
254 7877 100   7877   52707 sub _id_key { $_[0]->version < 7 ? 'id' : '$id' }
255              
256             sub _load_schema {
257 328     328   698 my ($self, $url) = @_;
258              
259 328 100       1559 if ($url =~ m!^https?://!) {
260 289         3507 warn "[JSON::Validator] Loading schema from URL $url\n" if DEBUG;
261 289         1107 return $self->_load_schema_from_url(Mojo::URL->new($url)->fragment(undef)),
262             "$url";
263             }
264              
265 39 100       394 if ($url =~ m!^data://([^/]*)/(.*)!) {
266 18         236 my ($class, $file) = ($1, $2);
267 18         103 my $text = data_section $class, $file, {confess => 1, encoding => 'UTF-8'};
268 17         1672 return $self->_load_schema_from_text(\$text), "$url";
269             }
270              
271 21 50       113 if ($url =~ m!^\s*[\[\{]!) {
272 0         0 warn "[JSON::Validator] Loading schema from string.\n" if DEBUG;
273 0         0 return $self->_load_schema_from_text(\$url), '';
274             }
275              
276 21         142 my $file = $url;
277 21         48 $file =~ s!^file://!!;
278 21         104 $file =~ s!#$!!;
279 21         111 $file = path(split '/', $file);
280 21 100 33     641 if (-e $file) {
    50          
281 20         554 $file = $file->realpath;
282 20         1261 warn "[JSON::Validator] Loading schema from file: $file\n" if DEBUG;
283 20         84 return $self->_load_schema_from_text(\$file->slurp),
284             CASE_TOLERANT ? path(lc $file) : $file;
285             }
286             elsif ($url =~ m!^/! and $self->ua->server->app) {
287 0         0 warn "[JSON::Validator] Loading schema from URL $url\n" if DEBUG;
288 0         0 return $self->_load_schema_from_url(Mojo::URL->new($url)->fragment(undef)),
289             "$url";
290             }
291              
292 1         52 confess "Unable to load schema '$url' ($file)";
293             }
294              
295             sub _load_schema_from_text {
296 327     327   32073 my ($self, $text) = @_;
297              
298             # JSON
299 327 50       2928 return Mojo::JSON::decode_json($$text) if $$text =~ /^\s*\{/s;
300              
301             # YAML
302 0 0       0 die "[JSON::Validator] YAML::XS 0.67 is missing or could not be loaded."
303             unless YAML_SUPPORT;
304              
305 49     49   374 no warnings 'once';
  49         1341  
  49         323728  
306 0         0 local $YAML::XS::Boolean = 'JSON::PP';
307 0         0 return YAML::XS::Load($$text);
308             }
309              
310             sub _load_schema_from_url {
311 292     292   46108 my ($self, $url) = @_;
312 292         881 my $cache_path = $self->cache_paths->[0];
313 292         1027 my $cache_file = Mojo::Util::md5_sum("$url");
314              
315 292         54252 for (@{$self->cache_paths}) {
  292         884  
316 288         2436 my $path = path $_, $cache_file;
317 288         7983 warn "[JSON::Validator] Looking for cached spec $path ($url)\n" if DEBUG;
318 288 100       1053 next unless -r $path;
319 275         11569 return $self->_load_schema_from_text(\$path->slurp);
320             }
321              
322 17         764 my $tx = $self->ua->get($url);
323 17   66     215081 my $err = $tx->error && $tx->error->{message};
324 17         352 confess "GET $url == $err" if DEBUG and $err;
325 17 100       93 die "[JSON::Validator] GET $url == $err" if $err;
326              
327 15 50 33     71 if ($cache_path
      66        
      33        
328             and
329             ($cache_path ne $BUNDLED_CACHE_DIR or $ENV{JSON_VALIDATOR_CACHE_ANYWAYS})
330             and -w $cache_path)
331             {
332 0         0 $cache_file = path $cache_path, $cache_file;
333             warn "[JSON::Validator] Caching $url to $cache_file\n"
334 0 0       0 unless $ENV{HARNESS_ACTIVE};
335 0         0 $cache_file->spurt($tx->res->body);
336             }
337              
338 15         276 return $self->_load_schema_from_text(\$tx->res->body);
339             }
340              
341             sub _node {
342 108     108   188 my ($node, $path, $offset, $create) = @_;
343              
344 108         140 my $n = 0;
345 108         196 while ($path->[$n]) {
346 139 100 100     426 $node->{$path->[$n]} ||= {} if $create;
347 139 100       346 return undef unless $node = $node->{$path->[$n]};
348 119 100       248 last if (++$n) + $offset >= @$path;
349             }
350              
351 88         231 return $node;
352             }
353              
354             sub _ref_to_schema {
355 1456     1456   3716 my ($self, $schema) = @_;
356              
357 1456         1987 my @guard;
358 1456         3796 while (my $tied = tied %$schema) {
359 1456         3665 push @guard, $tied->ref;
360 1456 50       3491 confess "Seems like you have a circular reference: @guard"
361             if @guard > RECURSION_LIMIT;
362 1456         3386 $schema = $tied->schema;
363 1456 100       3188 last if is_type $schema, 'BOOL';
364             }
365              
366 1456         3346 return $schema;
367             }
368              
369             sub _register_schema {
370 1203     1203   60086 my ($self, $schema, $fqn) = @_;
371 1203         4562 $fqn =~ s!(.)#$!$1!;
372 1203         4482 $self->{schemas}{$fqn} = $schema;
373             }
374              
375             # _resolve() method is used to convert all "id" into absolute URLs and
376             # resolve all the $ref's that we find inside JSON Schema specification.
377             sub _resolve {
378 7875     7875   15657 my ($self, $schema) = @_;
379 7875         17456 my $id_key = $self->_id_key;
380 7875         49236 my ($id, $resolved);
381              
382 7875   100     26761 local $self->{level} = $self->{level} || 0;
383 7875 100       17033 delete $_[0]->{schemas}{''} unless $self->{level};
384              
385 7875 100 50     29323 if (ref $schema eq 'HASH') {
    100          
    100          
386 586   100     2072 $id = $schema->{$id_key} // '';
387 586 50       1580 return $resolved if $resolved = $self->{schemas}{$id};
388             }
389             elsif ($resolved = $self->{schemas}{$schema // ''}) {
390 6953         1518700 return $resolved;
391             }
392             elsif (is_type $schema, 'BOOL') {
393 8         26 $self->_register_schema($schema, $schema);
394 8         63 return $schema;
395             }
396             else {
397 328         4524 ($schema, $id) = $self->_load_schema($schema);
398 324 100       1264236 $id = $schema->{$id_key} if $schema->{$id_key};
399             }
400              
401 910 100       2392 unless ($self->{level}) {
402 887   100     2775 my $rid = $schema->{$id_key} // $id;
403 887 100       2106 if ($rid) {
404 303 100       1489 confess "Root schema cannot have a fragment in the 'id'. ($rid)"
405             if $rid =~ /\#./;
406 302 100 100     2057 confess "Root schema cannot have a relative 'id'. ($rid)"
      66        
407             unless $rid =~ /^\w+:/
408             or -e $rid
409             or $rid =~ m!^/!;
410             }
411 884         1457 warn sprintf "[JSON::Validator] Using root_schema_url of '$rid'\n" if DEBUG;
412 884         1860 $self->{root_schema_url} = $rid;
413             }
414              
415 907         1389 $self->{level}++;
416 907         2954 $self->_register_schema($schema, $id);
417              
418 907         1542 my (%seen, @refs);
419             my @topics
420 907 100       2793 = ([$schema, is_type($id, 'Mojo::File') ? $id : Mojo::URL->new($id)]);
421 907         143293 while (@topics) {
422 39849         58146 my ($topic, $base) = @{shift @topics};
  39849         74696  
423              
424 39849 100       96084 if (is_type $topic, 'ARRAY') {
    100          
425 2772         4777 push @topics, map { [$_, $base] } @$topic;
  6319         15161  
426             }
427             elsif (is_type $topic, 'HASH') {
428 20585         65574 my $seen_addr = join ':', $base, refaddr($topic);
429 20585 50       3790930 next if $seen{$seen_addr}++;
430              
431             push @refs, [$topic, $base] and next
432 20585 100 50     81267 if $topic->{'$ref'} and !ref $topic->{'$ref'};
      100        
433              
434 13703 100 100     31128 if ($topic->{$id_key} and !ref $topic->{$id_key}) {
435 288         1119 my $fqn = Mojo::URL->new($topic->{$id_key});
436 288 100       31323 $fqn = $fqn->to_abs($base) unless $fqn->is_abs;
437 288         7825 $self->_register_schema($topic, $fqn->to_string);
438             }
439              
440 13703         40554 push @topics, map { [$_, $base] } values %$topic;
  32623         84806  
441             }
442             }
443              
444             # Need to register "id":"..." before resolving "$ref":"..."
445 907         3069 $self->_resolve_ref(@$_) for @refs;
446              
447 903         10178 return $schema;
448             }
449              
450             sub _location_to_abs {
451 6965     6965   12273 my ($location, $base) = @_;
452 6965         17700 my $location_as_url = Mojo::URL->new($location);
453 6965 100       1161154 return $location_as_url if $location_as_url->is_abs;
454              
455             # definitely relative now
456 6836 100       52351 if (is_type $base, 'Mojo::File') {
457 22 100       107 return $base if !length $location;
458 15         100 my $path = $base->sibling(split '/', $location)->realpath;
459 15         2189 return CASE_TOLERANT ? lc($path) : $path;
460             }
461 6814         18144 return $location_as_url->to_abs($base);
462             }
463              
464             sub _resolve_ref {
465 6882     6882   15228 my ($self, $topic, $url) = @_;
466 6882 100       13782 return if tied %$topic;
467              
468 6881         12011 my $other = $topic;
469 6881         10542 my ($fqn, $ref, @guard);
470              
471 6881         9450 while (1) {
472 13842 100       34205 last if is_type $other, 'BOOL';
473 13831         27441 $ref = $other->{'$ref'};
474 13831         24188 push @guard, $other->{'$ref'};
475 13831 50       27930 confess "Seems like you have a circular reference: @guard"
476             if @guard > RECURSION_LIMIT;
477 13831 100 66     44298 last if !$ref or ref $ref;
478 6965 50       17674 $fqn = $ref =~ m!^/! ? "#$ref" : $ref;
479 6965         24226 my ($location, $pointer) = split /#/, $fqn, 2;
480 6965         15656 $url = $location = _location_to_abs($location, $url);
481 6965 100 100     1889231 $pointer = undef if length $location and !length $pointer;
482 6965 100       1590329 $pointer = url_unescape $pointer if defined $pointer;
483 6965         48640 $fqn = join '#', grep defined, $location, $pointer;
484 6965         1496526 $other = $self->_resolve($location);
485              
486 6962 100 100     40687 if (defined $pointer and length $pointer and $pointer =~ m!^/!) {
      100        
487 4472         15949 $other = Mojo::JSON::Pointer->new($other)->get($pointer);
488 4472 100       191964 confess
489             qq[Possibly a typo in schema? Could not find "$pointer" in "$location" ($ref)]
490             if not defined $other;
491             }
492             }
493              
494 6877         30681 tie %$topic, 'JSON::Validator::Ref', $other, $topic->{'$ref'}, $fqn;
495             }
496              
497             sub _validate {
498 4956     4956   11496 my ($self, $data, $path, $schema) = @_;
499              
500             $schema = $self->_ref_to_schema($schema)
501 4956 100 100     20226 if ref $schema eq 'HASH' and $schema->{'$ref'};
502 4956 100       11621 return $schema ? () : E $path, [not => 'not'] if is_type $schema, 'BOOL';
    100          
503              
504             my $seen_addr = join ':', refaddr($schema),
505 4882 100       20872 (ref $data ? refaddr $data : ++$self->{seen}{scalar});
506              
507             # Avoid recursion
508 4882 100       11838 if ($self->{seen}{$seen_addr}) {
509 293         425 return @{$self->{seen}{$seen_addr}};
  293         729  
510             }
511              
512 4589         14666 $self->{seen}{$seen_addr} = \my @errors;
513 4589 100 100     13890 my $to_json
514             = (blessed $data and $data->can('TO_JSON')) ? \$data->TO_JSON : undef;
515 4589 100       8335 $data = $$to_json if $to_json;
516 4589   100     12540 my $type = $schema->{type} || schema_type $schema, $data;
517              
518             # Test base schema before allOf, anyOf or oneOf
519 4589 100       11229 if (ref $type eq 'ARRAY') {
    100          
520 97         148 push @{$self->{temp_schema}}, [map { +{%$schema, type => $_} } @$type];
  97         263  
  193         1430  
521             push @errors,
522             $self->_validate_any_of($to_json ? $$to_json : $_[1],
523 97 50       436 $path, $self->{temp_schema}[-1]);
524             }
525             elsif ($type) {
526 2822         10024 my $method = sprintf '_validate_type_%s', $type;
527 2822 100       11777 @errors = $self->$method($to_json ? $$to_json : $_[1], $path, $schema);
528 2822 100       7509 return @errors if @errors;
529             }
530              
531 4049 100       8346 if (exists $schema->{const}) {
532 32 50       119 push @errors,
533             $self->_validate_type_const($to_json ? $$to_json : $_[1], $path, $schema);
534 32 100       1076 return @errors if @errors;
535             }
536              
537 4037 100       7582 if ($schema->{enum}) {
538 505 50       1803 push @errors,
539             $self->_validate_type_enum($to_json ? $$to_json : $_[1], $path, $schema);
540 505 100       19968 return @errors if @errors;
541             }
542              
543 4015 100       7840 if (my $rules = $schema->{not}) {
544 11 50       44 push @errors, $self->_validate($to_json ? $$to_json : $_[1], $path, $rules);
545 11 100       57 return @errors ? () : (E $path, [not => 'not']);
546             }
547              
548 4004 100       7585 if (my $rules = $schema->{allOf}) {
549 111 50       500 push @errors,
550             $self->_validate_all_of($to_json ? $$to_json : $_[1], $path, $rules);
551             }
552              
553 4004 100       7634 if (my $rules = $schema->{anyOf}) {
554 569 50       1748 push @errors,
555             $self->_validate_any_of($to_json ? $$to_json : $_[1], $path, $rules);
556             }
557              
558 4004 100       7542 if (my $rules = $schema->{oneOf}) {
559 32 50       133 push @errors,
560             $self->_validate_one_of($to_json ? $$to_json : $_[1], $path, $rules);
561             }
562              
563 4004 100       6645 if ($schema->{if}) {
564             my $rules
565             = $self->_validate($data, $path, $schema->{if})
566             ? $schema->{else}
567 7 100       31 : $schema->{then};
568 7   50     26 push @errors, $self->_validate($data, $path, $rules // {});
569             }
570              
571 4004         9253 return @errors;
572             }
573              
574             sub _validate_all_of {
575 111     111   302 my ($self, $data, $path, $rules) = @_;
576 111         307 my $type = data_type $data, $rules;
577 111         236 my (@errors, @expected);
578              
579 111         175 my $i = 0;
580 111         256 for my $rule (@$rules) {
581 215 100       538 next unless my @e = $self->_validate($_[1], $path, $rule);
582 19         58 my $schema_type = schema_type $rule;
583 19 100       89 push @expected, $schema_type if $schema_type;
584 19 100 100     107 push @errors, [$i, @e] if !$schema_type or $schema_type eq $type;
585             }
586             continue {
587 215         461 $i++;
588             }
589              
590 111 100 100     500 return E $path, [allOf => type => join('/', uniq @expected), $type]
591             if !@errors and @expected;
592 110 100       264 return prefix_errors allOf => @errors if @errors;
593 96         195 return;
594             }
595              
596             sub _validate_any_of {
597 666     666   1553 my ($self, $data, $path, $rules) = @_;
598 666         1583 my $type = data_type $data, $rules;
599 666         1545 my (@e, @errors, @expected);
600              
601 666         1082 my $i = 0;
602 666         1419 for my $rule (@$rules) {
603 781         2185 @e = $self->_validate($_[1], $path, $rule);
604 781 100       2633 return unless @e;
605 131         421 my $schema_type = schema_type $rule;
606 131 100 50     654 push @errors, [$i, @e] and next if !$schema_type or $schema_type eq $type;
      100        
607 103         289 push @expected, $schema_type;
608             }
609             continue {
610 131         298 $i++;
611             }
612              
613 16         57 my $expected = join '/', uniq @expected;
614 16 100       117 return E $path, [anyOf => type => $expected, $type] unless @errors;
615 9         45 return prefix_errors anyOf => @errors;
616             }
617              
618             sub _validate_one_of {
619 32     32   82 my ($self, $data, $path, $rules) = @_;
620 32         95 my $type = data_type $data, $rules;
621 32         82 my (@errors, @expected);
622              
623 32         63 my ($i, @passed) = (0);
624 32         90 for my $rule (@$rules) {
625 67 100 50     194 my @e = $self->_validate($_[1], $path, $rule) or push @passed, $i and next;
626 37         94 my $schema_type = schema_type $rule;
627 37 100 50     198 push @errors, [$i, @e] and next if !$schema_type or $schema_type eq $type;
      100        
628 19         42 push @expected, $schema_type;
629             }
630             continue {
631 67         136 $i++;
632             }
633              
634 32 100       147 return if @passed == 1;
635 12 100       57 return E $path, [oneOf => 'all_rules_match'] unless @errors + @expected;
636 9 100       44 return E $path, [oneOf => 'n_rules_match', join(', ', @passed)] if @passed;
637 7 100       32 return prefix_errors oneOf => @errors if @errors;
638 1         4 return E $path, [oneOf => type => join('/', uniq @expected), $type];
639             }
640              
641             sub _validate_number_max {
642 380     380   994 my ($self, $value, $path, $schema, $expected) = @_;
643 380         517 my @errors;
644              
645 380   100     1516 my $cmp_with = $schema->{exclusiveMaximum} // '';
646 380 100       956 if (is_type $cmp_with, 'BOOL') {
    100          
647             push @errors, E $path,
648             [$expected => ex_maximum => $value, $schema->{maximum}]
649 4 100       25 unless $value < $schema->{maximum};
650             }
651             elsif (is_type $cmp_with, 'NUM') {
652 2 100       10 push @errors, E $path, [$expected => ex_maximum => $value, $cmp_with]
653             unless $value < $cmp_with;
654             }
655              
656 380 100       1104 if (exists $schema->{maximum}) {
657 32         64 my $cmp_with = $schema->{maximum};
658 32 100       138 push @errors, E $path, [$expected => maximum => $value, $cmp_with]
659             unless $value <= $cmp_with;
660             }
661              
662 380         2244 return @errors;
663             }
664              
665             sub _validate_number_min {
666 380     380   887 my ($self, $value, $path, $schema, $expected) = @_;
667 380         630 my @errors;
668              
669 380   100     1185 my $cmp_with = $schema->{exclusiveMinimum} // '';
670 380 100       948 if (is_type $cmp_with, 'BOOL') {
    100          
671             push @errors, E $path,
672             [$expected => ex_minimum => $value, $schema->{minimum}]
673 12 100       84 unless $value > $schema->{minimum};
674             }
675             elsif (is_type $cmp_with, 'NUM') {
676 2 100       10 push @errors, E $path, [$expected => ex_minimum => $value, $cmp_with]
677             unless $value > $cmp_with;
678             }
679              
680 380 100       1030 if (exists $schema->{minimum}) {
681 158         324 my $cmp_with = $schema->{minimum};
682 158 100       472 push @errors, E $path, [$expected => minimum => $value, $cmp_with]
683             unless $value >= $cmp_with;
684             }
685              
686 380         730 return @errors;
687             }
688              
689             sub _validate_type_enum {
690 524     524   1207 my ($self, $data, $path, $schema) = @_;
691 524         882 my $enum = $schema->{enum};
692 524         1261 my $m = data_checksum $data;
693              
694 524         34055 for my $i (@$enum) {
695 1979 100       59342 return if $m eq data_checksum $i;
696             }
697              
698             $enum = join ', ',
699 22 100 66     891 map { (!defined or ref) ? Mojo::JSON::encode_json($_) : $_ } @$enum;
  112         658  
700 22         308 return E $path, [enum => enum => $enum];
701             }
702              
703             sub _validate_type_const {
704 51     51   116 my ($self, $data, $path, $schema) = @_;
705 51         96 my $const = $schema->{const};
706              
707 51 100       127 return if data_checksum($data) eq data_checksum($const);
708 20         1116 return E $path, [const => const => Mojo::JSON::encode_json($const)];
709             }
710              
711             sub _validate_format {
712 233     233   494 my ($self, $value, $path, $schema) = @_;
713 233         683 my $code = $self->formats->{$schema->{format}};
714 233 100       1507 return do { warn "Format rule for '$schema->{format}' is missing"; return }
  1         43  
  1         7  
715             unless $code;
716 232 100       641 return unless my $err = $code->($value);
717 37         158 return E $path, [format => $schema->{format}, $err];
718             }
719              
720       8     sub _validate_type_any { }
721              
722             sub _validate_type_array {
723 271     271   609 my ($self, $data, $path, $schema) = @_;
724 271         424 my @errors;
725              
726 271 100       670 if (ref $data ne 'ARRAY') {
727 34         85 return E $path, [array => type => data_type $data];
728             }
729 237 100 100     935 if (defined $schema->{minItems} and $schema->{minItems} > @$data) {
730             push @errors, E $path,
731 4         27 [array => minItems => int(@$data), $schema->{minItems}];
732             }
733 237 100 100     638 if (defined $schema->{maxItems} and $schema->{maxItems} < @$data) {
734             push @errors, E $path,
735 4         26 [array => maxItems => int(@$data), $schema->{maxItems}];
736             }
737 237 100       716 if ($schema->{uniqueItems}) {
738 93         783 my %uniq;
739 93         236 for (@$data) {
740 177 100       5024 next if !$uniq{data_checksum($_)}++;
741 7         419 push @errors, E $path, [array => 'uniqueItems'];
742 7         23 last;
743             }
744             }
745              
746 237 100       5867 if ($schema->{contains}) {
    100          
    100          
747 2         5 my @e;
748 2         6 for my $i (0 .. @$data - 1) {
749 4         18 my @tmp = $self->_validate($data->[$i], "$path/$i", $schema->{contains});
750 4 100       13 push @e, \@tmp if @tmp;
751             }
752 2 100       10 push @errors, map {@$_} @e if @e >= @$data;
  2         5  
753             }
754             elsif (ref $schema->{items} eq 'ARRAY') {
755 17   100     102 my $additional_items = $schema->{additionalItems} // {type => 'any'};
756 17         40 my @rules = @{$schema->{items}};
  17         49  
757              
758 17 100       68 if ($additional_items) {
759 14         52 push @rules, $additional_items while @rules < @$data;
760             }
761              
762 17 100       76 if (@rules == @$data) {
    100          
763 13         54 for my $i (0 .. @rules - 1) {
764 38         179 push @errors, $self->_validate($data->[$i], "$path/$i", $rules[$i]);
765             }
766             }
767             elsif (!$additional_items) {
768 2         25 push @errors, E $path,
769             [array => additionalItems => int(@$data), int(@rules)];
770             }
771             }
772             elsif (exists $schema->{items}) {
773 159         584 for my $i (0 .. @$data - 1) {
774 280         1391 push @errors, $self->_validate($data->[$i], "$path/$i", $schema->{items});
775             }
776             }
777              
778 237         553 return @errors;
779             }
780              
781             sub _validate_type_boolean {
782 146     146   408 my ($self, $value, $path, $schema) = @_;
783 146 100       364 return if is_type $value, 'BOOL';
784              
785             # String that looks like a boolean
786 81 100 100     602 if (
      100        
      100        
787             defined $value
788             and $self->{coerce}{booleans}
789             and (B::svref_2object(\$value)->FLAGS & (B::SVp_IOK | B::SVp_NOK)
790             or $value =~ /^(true|false)$/)
791             )
792             {
793 27 100       103 $_[1] = $value ? true : false;
794 27         187 return;
795             }
796              
797 54         133 return E $path, [boolean => type => data_type $value];
798             }
799              
800             sub _validate_type_integer {
801 254     254   688 my ($self, $value, $path, $schema) = @_;
802 254         756 my @errors = $self->_validate_type_number($_[1], $path, $schema, 'integer');
803              
804 254 100       707 return @errors if @errors;
805 209 100       1349 return if $value =~ /^-?\d+$/;
806 6         22 return E $path, [integer => type => data_type $value];
807             }
808              
809             sub _validate_type_null {
810 14     14   45 my ($self, $value, $path, $schema) = @_;
811              
812 14 100       52 return unless defined $value;
813 8         39 return E $path, ['null', 'null'];
814             }
815              
816             sub _validate_type_number {
817 432     432   1065 my ($self, $value, $path, $schema, $expected) = @_;
818 432         645 my @errors;
819              
820 432   100     1307 $expected ||= 'number';
821              
822 432 100 100     1723 if (!defined $value or ref $value) {
823 17         73 return E $path, [$expected => type => data_type $value];
824             }
825 415 100       1040 unless (is_type $value, 'NUM') {
826             return E $path, [$expected => type => data_type $value]
827             if !$self->{coerce}{numbers}
828 85 100 100     741 or $value !~ /^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$/;
829 50         191 $_[1] = 0 + $value; # coerce input value
830             }
831              
832             push @errors, $self->_validate_format($value, $path, $schema)
833 380 50       1152 if $schema->{format};
834 380         1226 push @errors, $self->_validate_number_max($value, $path, $schema, $expected);
835 380         951 push @errors, $self->_validate_number_min($value, $path, $schema, $expected);
836              
837 380         655 my $d = $schema->{multipleOf};
838 380 100 100     1128 push @errors, E $path, [$expected => multipleOf => $d]
839             if $d and ($value / $d) =~ /\.[^0]+$/;
840              
841 380         723 return @errors;
842             }
843              
844             sub _validate_type_object {
845 1399     1399   3103 my ($self, $data, $path, $schema) = @_;
846              
847 1399 100       3366 if (ref $data ne 'HASH') {
848 44         174 return E $path, [object => type => data_type $data];
849             }
850              
851 1355         1902 my @errors;
852 1355         6064 my @dkeys = sort keys %$data;
853 1355 100 100     4468 if (defined $schema->{maxProperties} and $schema->{maxProperties} < @dkeys) {
854             push @errors, E $path,
855 2         16 [object => maxProperties => int(@dkeys), $schema->{maxProperties}];
856             }
857 1355 100 100     3599 if (defined $schema->{minProperties} and $schema->{minProperties} > @dkeys) {
858             push @errors, E $path,
859 3         20 [object => minProperties => int(@dkeys), $schema->{minProperties}];
860             }
861 1355 100       3114 if (my $n_schema = $schema->{propertyNames}) {
862 3         9 for my $name (keys %$data) {
863 4 100       10 next unless my @e = $self->_validate($name, $path, $n_schema);
864 1         3 push @errors, prefix_errors propertyName => [map { ($name, $_) } @e];
  1         5  
865             }
866             }
867              
868 1355         1917 my %rules;
869 1355         1898 for my $k (keys %{$schema->{properties}}) {
  1355         10111  
870 28074         44963 my $r = $schema->{properties}{$k};
871 28074         33701 push @{$rules{$k}}, $r;
  28074         53687  
872 28074 100 100     57517 if ( $self->{coerce}{defaults}
      100        
      100        
873             and ref $r eq 'HASH'
874             and exists $r->{default}
875             and !exists $data->{$k})
876             {
877 18         56 $data->{$k} = $r->{default};
878             }
879             }
880              
881 1355 100       3596 for my $p (keys %{$schema->{patternProperties} || {}}) {
  1355         6561  
882 53         130 my $r = $schema->{patternProperties}{$p};
883 53         101 push @{$rules{$_}}, $r for sort grep { $_ =~ /$p/ } @dkeys;
  81         848  
  37         154  
884             }
885              
886             my $additional
887             = exists $schema->{additionalProperties}
888             ? $schema->{additionalProperties}
889 1355 100       4565 : {};
890 1355 100       2661 if ($additional) {
    100          
891 1343 100       3950 $additional = {} unless is_type $additional, 'HASH';
892 1343   100     7244 $rules{$_} ||= [$additional] for @dkeys;
893             }
894 20         152 elsif (my @k = grep { !$rules{$_} } @dkeys) {
895 6         16 local $" = ', ';
896 6         36 return E $path, [object => additionalProperties => join '/', @k];
897             }
898              
899 1349 100       2638 for my $k (sort uniq @{$schema->{required} || []}) {
  1349         7010  
900 102 100       362 next if exists $data->{$k};
901 25         122 push @errors, E json_pointer($path, $k), [object => 'required'];
902 25         90 delete $rules{$k}; # why bother?
903             }
904              
905 1349   100     4376 my $dependencies = $schema->{dependencies} || {};
906 1349         3730 for my $k (keys %$dependencies) {
907 1540 100       3955 next if not exists $data->{$k};
908 10 100       56 if (ref $dependencies->{$k} eq 'ARRAY') {
    50          
909             push @errors,
910 1         4 map { E json_pointer($path, $_), [object => dependencies => $k] }
911 9         24 grep { !exists $data->{$_} } @{$dependencies->{$k}};
  9         39  
  9         29  
912             }
913             elsif (ref $dependencies->{$k} eq 'HASH') {
914             push @errors,
915 1         9 $self->_validate_type_object($data, $path, $schema->{dependencies}{$k});
916             }
917             }
918              
919 1349         14199 for my $k (sort keys %rules) {
920 28766         35038 for my $r (@{$rules{$k}}) {
  28766         49250  
921 28774 100       61637 next unless exists $data->{$k};
922 2614 100 100     11355 $r = $self->_ref_to_schema($r) if ref $r eq 'HASH' and $r->{'$ref'};
923 2614         8284 my @e = $self->_validate($data->{$k}, json_pointer($path, $k), $r);
924 2614         5423 push @errors, @e;
925 2614 100 100     8307 next if @e or !is_type $r, 'HASH';
926             push @errors,
927             $self->_validate_type_enum($data->{$k}, json_pointer($path, $k), $r)
928 2454 100       5432 if $r->{enum};
929             push @errors,
930             $self->_validate_type_const($data->{$k}, json_pointer($path, $k), $r)
931 2454 100       7468 if $r->{const};
932             }
933             }
934              
935 1349         10371 return @errors;
936             }
937              
938             sub _validate_type_string {
939 537     537   1278 my ($self, $value, $path, $schema) = @_;
940 537         813 my @errors;
941              
942 537 100 100     2322 if (!defined $value or ref $value) {
943 16         63 return E $path, [string => type => data_type $value];
944             }
945 521 50 66     3371 if ( B::svref_2object(\$value)->FLAGS & (B::SVp_IOK | B::SVp_NOK)
      66        
946             and 0 + $value eq $value
947             and $value * 0 == 0)
948             {
949             return E $path, [string => type => data_type $value]
950 22 100       178 unless $self->{coerce}{strings};
951 1         10 $_[1] = "$value"; # coerce input value
952             }
953 500 100       1409 if ($schema->{format}) {
954 233         726 push @errors, $self->_validate_format($value, $path, $schema);
955             }
956 500 100       1170 if (defined $schema->{maxLength}) {
957 38 100       128 if (length($value) > $schema->{maxLength}) {
958             push @errors, E $path,
959 14         90 [string => maxLength => length($value), $schema->{maxLength}];
960             }
961             }
962 500 100       1081 if (defined $schema->{minLength}) {
963 28 100       80 if (length($value) < $schema->{minLength}) {
964             push @errors, E $path,
965 4         22 [string => minLength => length($value), $schema->{minLength}];
966             }
967             }
968 500 100       1055 if (defined $schema->{pattern}) {
969 35         64 my $p = $schema->{pattern};
970 35 100       650 push @errors, E $path, [string => pattern => $p] unless $value =~ /$p/;
971             }
972              
973 500         1163 return @errors;
974             }
975              
976             1;
977              
978             =encoding utf8
979              
980             =head1 NAME
981              
982             JSON::Validator - Validate data against a JSON schema
983              
984             =head1 SYNOPSIS
985              
986             use JSON::Validator;
987             my $jv = JSON::Validator->new;
988              
989             # Define a schema - http://json-schema.org/learn/miscellaneous-examples.html
990             # You can also load schema from disk or web
991             $jv->schema({
992             type => "object",
993             required => ["firstName", "lastName"],
994             properties => {
995             firstName => {type => "string"},
996             lastName => {type => "string"},
997             age => {type => "integer", minimum => 0, description => "Age in years"}
998             }
999             });
1000              
1001             # Validate your data
1002             my @errors = $jv->validate({firstName => "Jan Henning", lastName => "Thorsen", age => -42});
1003              
1004             # Do something if any errors was found
1005             die "@errors" if @errors;
1006              
1007             # Use joi() to build the schema
1008             use JSON::Validator 'joi';
1009              
1010             $jv->schema(joi->object->props({
1011             firstName => joi->string->required,
1012             lastName => joi->string->required,
1013             age => joi->integer->min(0),
1014             }));
1015              
1016             # joi() can also validate directly
1017             my @errors = joi(
1018             {firstName => "Jan Henning", lastName => "Thorsen", age => -42},
1019             joi->object->props({
1020             firstName => joi->string->required,
1021             lastName => joi->string->required,
1022             age => joi->integer->min(0),
1023             });
1024             );
1025              
1026             =head1 DESCRIPTION
1027              
1028             L is a data structure validation library based around
1029             L. This module can be used directly with
1030             a JSON schema or you can use the elegant DSL schema-builder
1031             L to define the schema programmatically.
1032              
1033             =head2 Supported schema formats
1034              
1035             L can load JSON schemas in multiple formats: Plain perl data
1036             structured (as shown in L), JSON or YAML. The JSON parsing is done
1037             with L, while YAML files require the optional module L to
1038             be installed.
1039              
1040             =head2 Resources
1041              
1042             Here are some resources that are related to JSON schemas and validation:
1043              
1044             =over 4
1045              
1046             =item * L
1047              
1048             =item * L
1049              
1050             =item * L
1051              
1052             =back
1053              
1054             =head2 Bundled specifications
1055              
1056             This module comes with some JSON specifications bundled, so your application
1057             don't have to fetch those from the web. These specifications should be up to
1058             date, but please submit an issue if they are not.
1059              
1060             Files referenced to an URL will automatically be cached if the first element in
1061             L is a writable directory. Note that the cache headers for the
1062             remote assets are B honored, so you will manually need to remove any
1063             cached file, should you need to refresh them.
1064              
1065             To download and cache an online asset, do this:
1066              
1067             JSON_VALIDATOR_CACHE_PATH=/some/writable/directory perl myapp.pl
1068              
1069             Here is the list of the bundled specifications:
1070              
1071             =over 2
1072              
1073             =item * JSON schema, draft 4, 6, 7
1074              
1075             Web page: L
1076              
1077             C<$ref>: L,
1078             L,
1079             L.
1080              
1081             =item * JSON schema for JSONPatch files
1082              
1083             Web page: L
1084              
1085             C<$ref>: L
1086              
1087             =item * Swagger / OpenAPI specification, version 2
1088              
1089             Web page: L
1090              
1091             C<$ref>: L
1092              
1093             =item * OpenAPI specification, version 3
1094              
1095             Web page: L
1096              
1097             C<$ref>: L
1098              
1099             This specification is still EXPERIMENTAL.
1100              
1101             =item * Swagger Petstore
1102              
1103             This is used for unit tests, and should not be relied on by external users.
1104              
1105             =back
1106              
1107             =head1 ERROR OBJECT
1108              
1109             The methods L and the function L returns a list of
1110             L objects when the input data violates the L.
1111              
1112             =head1 FUNCTIONS
1113              
1114             =head2 joi
1115              
1116             use JSON::Validator "joi";
1117             my $joi = joi;
1118             my @errors = joi($data, $joi); # same as $joi->validate($data);
1119              
1120             Used to construct a new L object or perform validation.
1121              
1122             =head2 validate_json
1123              
1124             use JSON::Validator "validate_json";
1125             my @errors = validate_json $data, $schema;
1126              
1127             This can be useful in web applications:
1128              
1129             my @errors = validate_json $c->req->json, "data://main/spec.json";
1130              
1131             See also L and L for more details.
1132              
1133             =head1 ATTRIBUTES
1134              
1135             =head2 cache_paths
1136              
1137             my $jv = $jv->cache_paths(\@paths);
1138             my $array_ref = $jv->cache_paths;
1139              
1140             A list of directories to where cached specifications are stored. Defaults to
1141             C environment variable and the specs that is bundled
1142             with this distribution.
1143              
1144             C can be a list of directories, each separated by ":".
1145              
1146             See L for more details.
1147              
1148             =head2 formats
1149              
1150             my $hash_ref = $jv->formats;
1151             my $jv = $jv->formats(\%hash);
1152              
1153             Holds a hash-ref, where the keys are supported JSON type "formats", and
1154             the values holds a code block which can validate a given format. A code
1155             block should return C on success and an error string on error:
1156              
1157             sub { return defined $_[0] && $_[0] eq "42" ? undef : "Not the answer." };
1158              
1159             See L for a list of supported formats.
1160              
1161             =head2 generate_definitions_path
1162              
1163             my $cb = $jv->generate_definitions_path;
1164             my $jv = $jv->generate_definitions_path(sub { my $ref = shift; return ["definitions"] });
1165              
1166             Holds a callback that is used by L to figure out where to place
1167             references. The default location is under "definitions", but this can be
1168             changed to whatever you want. The input C<$ref> variable passed on is a
1169             L object.
1170              
1171             This attribute is EXPERIMENTAL and might change without warning.
1172              
1173             =head2 ua
1174              
1175             my $ua = $jv->ua;
1176             my $jv = $jv->ua(Mojo::UserAgent->new);
1177              
1178             Holds a L object, used by L to load a JSON schema
1179             from remote location.
1180              
1181             The default L will detect proxy settings and have
1182             L set to 3.
1183              
1184             =head2 version
1185              
1186             my $int = $jv->version;
1187             my $jv = $jv->version(7);
1188              
1189             Used to set the JSON Schema version to use. Will be set automatically when
1190             using L, unless already set.
1191              
1192             =head1 METHODS
1193              
1194             =head2 bundle
1195              
1196             # These two lines does the same
1197             my $schema = $jv->bundle({schema => $jv->schema->data});
1198             my $schema = $jv->bundle;
1199              
1200             # Will only bundle a section of the schema
1201             my $schema = $jv->bundle({schema => $jv->schema->get("/properties/person/age")});
1202              
1203             Used to create a new schema, where there are no "$ref" pointing to external
1204             resources. This means that all the "$ref" that are found, will be moved into
1205             the "definitions" key, in the returned C<$schema>.
1206              
1207             =head2 coerce
1208              
1209             my $jv = $jv->coerce('bool,def,num,str');
1210             my $jv = $jv->coerce('booleans,defaults,numbers,strings');
1211             my $hash_ref = $jv->coerce;
1212              
1213             Set the given type to coerce. Before enabling coercion this module is very
1214             strict when it comes to validating types. Example: The string C<"1"> is not
1215             the same as the number C<1>, unless you have "numbers" coercion enabled.
1216              
1217             =over 2
1218              
1219             =item * booleans
1220              
1221             Will convert what looks can be interpreted as a boolean (that is, an actual
1222             numeric C<1> or C<0>, and the strings "true" and "false") to a
1223             L object. Note that "foo" is not considered a true value and
1224             will fail the validation.
1225              
1226             =item * defaults
1227              
1228             Will copy the default value defined in the schema, into the input structure,
1229             if the input value is non-existing.
1230              
1231             Note that support for "default" is currently EXPERIMENTAL, and enabling this
1232             might be changed in future versions.
1233              
1234             =item * numbers
1235              
1236             Will convert strings that looks like numbers, into true numbers. This works for
1237             both the "integer" and "number" types.
1238              
1239             =item * strings
1240              
1241             Will convert a number into a string. This works for the "string" type.
1242              
1243             =back
1244              
1245             =head2 get
1246              
1247             my $sub_schema = $jv->get("/x/y");
1248             my $sub_schema = $jv->get(["x", "y"]);
1249              
1250             Extract value from L identified by the given JSON Pointer. Will at the
1251             same time resolve C<$ref> if found. Example:
1252              
1253             $jv->schema({x => {'$ref' => '#/y'}, y => {'type' => 'string'}});
1254             $jv->schema->get('/x') == {'$ref' => '#/y'}
1255             $jv->schema->get('/x')->{'$ref'} == '#/y'
1256             $jv->get('/x') == {type => 'string'}
1257              
1258             The argument can also be an array-ref with the different parts of the pointer
1259             as each elements.
1260              
1261             =head2 new
1262              
1263             $jv = JSON::Validator->new(%attributes);
1264             $jv = JSON::Validator->new(\%attributes);
1265              
1266             Creates a new L object.
1267              
1268             =head2 load_and_validate_schema
1269              
1270             my $jv = $jv->load_and_validate_schema($schema, \%args);
1271              
1272             Will load and validate C<$schema> against the OpenAPI specification. C<$schema>
1273             can be anything L accepts. The expanded specification
1274             will be stored in L on success. See
1275             L for the different version of C<$url> that can be
1276             accepted.
1277              
1278             C<%args> can be used to further instruct the validation process:
1279              
1280             =over 2
1281              
1282             =item * schema
1283              
1284             Defaults to "http://json-schema.org/draft-04/schema#", but can be any
1285             structured that can be used to validate C<$schema>.
1286              
1287             =back
1288              
1289             =head2 schema
1290              
1291             my $jv = $jv->schema($json_or_yaml_string);
1292             my $jv = $jv->schema($url);
1293             my $jv = $jv->schema(\%schema);
1294             my $jv = $jv->schema(JSON::Validator::Joi->new);
1295             my $schema = $jv->schema;
1296              
1297             Used to set a schema from either a data structure or a URL.
1298              
1299             C<$schema> will be a L object when loaded,
1300             and C by default.
1301              
1302             The C<$url> can take many forms, but needs to point to a text file in the
1303             JSON or YAML format.
1304              
1305             =over 4
1306              
1307             =item * file://...
1308              
1309             A file on disk. Note that it is required to use the "file" scheme if you want
1310             to reference absolute paths on your file system.
1311              
1312             =item * http://... or https://...
1313              
1314             A web resource will be fetched using the L, stored in L.
1315              
1316             =item * data://Some::Module/spec.json
1317              
1318             Will load a given "spec.json" file from C using
1319             L.
1320              
1321             =item * data:///spec.json
1322              
1323             A "data" URL without a module name will use the current package and search up
1324             the call/inheritance tree.
1325              
1326             =item * Any other URL
1327              
1328             An URL (without a recognized scheme) will be treated as a path to a file on
1329             disk.
1330              
1331             =back
1332              
1333             =head2 singleton
1334              
1335             my $jv = JSON::Validator->singleton;
1336              
1337             Returns the L object used by L.
1338              
1339             =head2 validate
1340              
1341             my @errors = $jv->validate($data);
1342             my @errors = $jv->validate($data, $schema);
1343              
1344             Validates C<$data> against a given JSON L. C<@errors> will
1345             contain validation error objects or be an empty list on success.
1346              
1347             See L for details.
1348              
1349             C<$schema> is optional, but when specified, it will override schema stored in
1350             L. Example:
1351              
1352             $jv->validate({hero => "superwoman"}, {type => "object"});
1353              
1354             =head2 SEE ALSO
1355              
1356             =over 2
1357              
1358             =item * L
1359              
1360             L is a plugin for L that utilize
1361             L and the L
1362             to build routes with input and output validation.
1363              
1364             =back
1365              
1366             =head1 COPYRIGHT AND LICENSE
1367              
1368             Copyright (C) 2014-2018, Jan Henning Thorsen
1369              
1370             This program is free software, you can redistribute it and/or modify it under
1371             the terms of the Artistic License version 2.0.
1372              
1373             =head1 AUTHOR
1374              
1375             Jan Henning Thorsen - C
1376              
1377             Daniel Böhmer - C
1378              
1379             Ed J - C
1380              
1381             Karen Etheridge - C
1382              
1383             Kevin Goess - C
1384              
1385             Martin Renvoize - C
1386              
1387             =cut