File Coverage

blib/lib/JSON/Validator.pm
Criterion Covered Total %
statement 549 565 97.1
branch 344 376 91.4
condition 162 190 85.2
subroutine 64 65 98.4
pod 10 10 100.0
total 1129 1206 93.6


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