File Coverage

blib/lib/JSON/Validator.pm
Criterion Covered Total %
statement 535 551 97.1
branch 339 370 91.6
condition 161 190 84.7
subroutine 63 64 98.4
pod 10 10 100.0
total 1108 1185 93.5


line stmt bran cond sub pod time code
1             package JSON::Validator;
2 48     48   7923766 use Mojo::Base -base;
  48         485  
  48         239  
3              
4 48     48   6145 use B;
  48         81  
  48         2103  
5 48     48   227 use Carp 'confess';
  48         68  
  48         1903  
6 48     48   221 use Exporter 'import';
  48         72  
  48         1072  
7 48     48   16106 use JSON::Validator::Error;
  48         103  
  48         281  
8 48     48   17788 use JSON::Validator::Formats;
  48         116  
  48         1548  
9 48     48   26348 use JSON::Validator::Joi;
  48         205  
  48         461  
10 48     48   21122 use JSON::Validator::Ref;
  48         127  
  48         1501  
11             use JSON::Validator::Util
12 48     48   286 qw(E data_checksum data_section data_type is_type json_pointer prefix_errors schema_type uniq);
  48         88  
  48         2968  
13 48     48   270 use Mojo::File 'path';
  48         89  
  48         1633  
14 48     48   18089 use Mojo::JSON::Pointer;
  48         25567  
  48         322  
15 48     48   1540 use Mojo::JSON qw(false true);
  48         98  
  48         4538  
16 48     48   17884 use Mojo::URL;
  48         290106  
  48         335  
17 48     48   1953 use Mojo::Util qw(url_unescape sha1_sum);
  48         109  
  48         2052  
18 48     48   259 use Scalar::Util qw(blessed refaddr);
  48         85  
  48         2385  
19              
20 48     48   253 use constant CASE_TOLERANT => File::Spec->case_tolerant;
  48         86  
  48         3992  
21 48   50 48   258 use constant DEBUG => $ENV{JSON_VALIDATOR_DEBUG} || 0;
  48         87  
  48         2678  
22 48   50 48   267 use constant RECURSION_LIMIT => $ENV{JSON_VALIDATOR_RECURSION_LIMIT} || 100;
  48         108  
  48         2529  
23 48     48   260 use constant SPECIFICATION_URL => 'http://json-schema.org/draft-04/schema#';
  48         93  
  48         2415  
24 48     48   280 use constant YAML_SUPPORT => eval 'use YAML::XS 0.67;1';
  48     48   125  
  48         3604  
  48         126489  
  0         0  
  0         0  
25              
26             our $VERSION = '3.21';
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 7188 my ($self, $args) = @_;
57 15         20 my ($cloner, $tied);
58              
59             my $schema
60 15 100       40 = $args->{schema} ? $self->_resolve($args->{schema}) : $self->schema->data;
61 15         60 my @topics = ([$schema, my $bundle = {}, '']); # ([$from, $to], ...);
62              
63 15 100       27 if ($args->{replace}) {
64             $cloner = sub {
65 15     15   20 my $from = shift;
66 15         19 my $from_type = ref $from;
67 15 100 100     55 $from = $tied->schema if $from_type eq 'HASH' and $tied = tied %$from;
68 15 100       32 my $to = $from_type eq 'ARRAY' ? [] : $from_type eq 'HASH' ? {} : $from;
    50          
69 15 100       25 push @topics, [$from, $to] if $from_type;
70 15         40 return $to;
71 3         13 };
72             }
73             else {
74             $cloner = sub {
75 178     178   210 my $from = shift;
76 178         205 my $from_type = ref $from;
77              
78 178   100     321 $tied = $from_type eq 'HASH' && tied %$from;
79 178 100       251 unless ($tied) {
80 143 100       226 my $to = $from_type eq 'ARRAY' ? [] : $from_type eq 'HASH' ? {} : $from;
    50          
81 143 100       226 push @topics, [$from, $to] if $from_type;
82 143         353 return $to;
83             }
84              
85             return $from
86             if !$args->{schema}
87 35 100 100     92 and $tied->fqn =~ m!^\Q$self->{root_schema_url}\E\#!;
88              
89 27         224 my $path = $self->_definitions_path($bundle, $tied);
90 27 50       78 unless ($self->{bundled_refs}{$tied->fqn}++) {
91 27   100     56 push @topics,
92             [_node($schema, $path, 1, 0) || {}, _node($bundle, $path, 1, 1)];
93 27         59 push @topics, [$tied->schema, _node($bundle, $path, 0, 1)];
94             }
95              
96 27         69 $path = join '/', '#', @$path;
97 27         59 tie my %ref, 'JSON::Validator::Ref', $tied->schema, $path;
98 27         106 return \%ref;
99 12         55 };
100             }
101              
102             Mojo::Util::deprecated('bundle({ref_key => "..."}) will be removed.')
103 15 50       33 if $args->{ref_key};
104 15         29 local $self->{definitions_key} = $args->{ref_key};
105 15         27 local $self->{bundled_refs} = {};
106              
107 15         27 while (@topics) {
108 130         171 my ($from, $to) = @{shift @topics};
  130         201  
109 130 50       314 if (ref $from eq 'ARRAY') {
    50          
110 0         0 for (my $i = 0; $i < @$from; $i++) {
111 0         0 $to->[$i] = $cloner->($from->[$i]);
112             }
113             }
114             elsif (ref $from eq 'HASH') {
115 130         255 for my $key (keys %$from) {
116 208   100     565 $to->{$key} //= $cloner->($from->{$key});
117             }
118             }
119             }
120              
121 15         174 return $bundle;
122             }
123              
124             sub coerce {
125 19     19 1 510 my $self = shift;
126 19 100 50     89 return $self->{coerce} ||= {} unless defined(my $what = shift);
127              
128 14 100       47 if ($what eq '1') {
129 1         6 Mojo::Util::deprecated('coerce(1) will be deprecated.');
130 1         272 $what = {booleans => 1, numbers => 1, strings => 1};
131             }
132              
133 14         66 state $short = {bool => 'booleans', def => 'defaults', num => 'numbers',
134             str => 'strings'};
135              
136 14 100       70 $what = {map { ($_ => 1) } split /,/, $what} unless ref $what;
  10         39  
137 14         48 $self->{coerce} = {};
138 14   66     117 $self->{coerce}{($short->{$_} || $_)} = $what->{$_} for keys %$what;
139              
140 14         42 return $self;
141             }
142              
143 10     10 1 2066 sub get { JSON::Validator::Util::schema_extract(shift->schema->data, shift) }
144              
145             sub joi {
146 52 100   52 1 25887 return JSON::Validator::Joi->new unless @_;
147 2         3 my ($data, $joi) = @_;
148 2         7 return $joi->validate($data, $joi);
149             }
150              
151             sub load_and_validate_schema {
152 270     270 1 28326 my ($self, $spec, $args) = @_;
153 270   100     1536 my $schema = $args->{schema} || SPECIFICATION_URL;
154 270 100 66     3196 $self->version($1) if !$self->{version} and $schema =~ /draft-0+(\w+)/;
155 270         2553 $spec = $self->_resolve($spec);
156 265         1323 my @errors = $self->new(%$self)->schema($schema)->validate($spec);
157 265 100       2054 confess join "\n", "Invalid JSON specification $spec:", map {"- $_"} @errors
  1         6  
158             if @errors;
159 264         1101 $self->{schema} = Mojo::JSON::Pointer->new($spec);
160 264         4389 $self;
161             }
162              
163             sub new {
164 580     580 1 449650 my $self = shift->SUPER::new(@_);
165 580 100       5313 $self->coerce($self->{coerce}) if defined $self->{coerce};
166 580         2482 return $self;
167             }
168              
169             sub schema {
170 1530     1530 1 147248 my $self = shift;
171 1530 100       4696 return $self->{schema} unless @_;
172 624         1573 $self->{schema} = Mojo::JSON::Pointer->new($self->_resolve(shift));
173 621         7571 return $self;
174             }
175              
176 9     9 1 31 sub singleton { state $jv = shift->new }
177              
178             sub validate {
179 917     917 1 27536 my ($self, $data, $schema) = @_;
180 917   100     4004 $schema ||= $self->schema->data;
181 917 50       5600 return E '/', 'No validation rules defined.' unless defined $schema;
182              
183 917         2292 local $self->{schema} = Mojo::JSON::Pointer->new($schema);
184 917         8228 local $self->{seen} = {};
185 917         2520 local $self->{temp_schema} = []; # make sure random-errors.t does not fail
186 917         3465 my @errors = $self->_validate($_[1], '', $schema);
187 917         6800 return @errors;
188             }
189              
190             sub validate_json {
191 9     9 1 60855 __PACKAGE__->singleton->schema($_[1])->validate($_[0]);
192             }
193              
194             sub _build_formats {
195             return {
196 17     17   1178 'date' => JSON::Validator::Formats->can('check_date'),
197             'date-time' => JSON::Validator::Formats->can('check_date_time'),
198             'email' => JSON::Validator::Formats->can('check_email'),
199             'hostname' => JSON::Validator::Formats->can('check_hostname'),
200             'idn-email' => JSON::Validator::Formats->can('check_idn_email'),
201             'idn-hostname' => JSON::Validator::Formats->can('check_idn_hostname'),
202             'ipv4' => JSON::Validator::Formats->can('check_ipv4'),
203             'ipv6' => JSON::Validator::Formats->can('check_ipv6'),
204             'iri' => JSON::Validator::Formats->can('check_iri'),
205             'iri-reference' => JSON::Validator::Formats->can('check_iri_reference'),
206             'json-pointer' => JSON::Validator::Formats->can('check_json_pointer'),
207             'regex' => JSON::Validator::Formats->can('check_regex'),
208             'relative-json-pointer' =>
209             JSON::Validator::Formats->can('check_relative_json_pointer'),
210             'time' => JSON::Validator::Formats->can('check_time'),
211             'uri' => JSON::Validator::Formats->can('check_uri'),
212             'uri-reference' => JSON::Validator::Formats->can('check_uri_reference'),
213             'uri-reference' => JSON::Validator::Formats->can('check_uri_reference'),
214             'uri-template' => JSON::Validator::Formats->can('check_uri_template'),
215             };
216             }
217              
218             sub _definitions_path {
219 27     27   39 my ($self, $bundle, $ref) = @_;
220 27         73 my $path = $self->generate_definitions_path->($ref);
221              
222             # No need to rewrite, if it already has a nice name
223 27         62 my $node = _node($bundle, $path, 2, 0);
224 27         50 my $prefix = join '/', @$path;
225 27 100       50 if ($ref->fqn =~ m!#/$prefix/([^/]+)$!) {
226 4         9 my $key = $1;
227              
228 4 50 66     22 if ( $self->{bundled_refs}{$ref->fqn}
      66        
      33        
229             or !$node
230             or !$node->{$key}
231             or data_checksum($ref->schema) eq data_checksum($node->{$key}))
232             {
233 4         15 return [@$path, $key];
234             }
235             }
236              
237             # Generate definitions key based on filename
238 23         52 my ($spec_path, $fragment) = split '#', $ref->fqn;
239 23         36 my $key = $fragment;
240 23 50       337 if (-e $spec_path) {
241 23         79 $key = join '-', map { s!^\W+!!; $_ } grep {$_} path($spec_path)->basename,
  51         93  
  51         100  
  69         1176  
242             $fragment, substr(sha1_sum($spec_path), 0, 10);
243             }
244              
245             # Fallback or nicer path name
246 23         102 $key =~ s![^\w-]!_!g;
247 23         68 return [@$path, $key];
248             }
249              
250             # Try not to break JSON::Validator::OpenAPI::Mojolicious
251 0     0   0 sub _get { shift; JSON::Validator::Util::_schema_extract(@_) }
  0         0  
252              
253 7865 100   7865   49185 sub _id_key { $_[0]->version < 7 ? 'id' : '$id' }
254              
255             sub _load_schema {
256 328     328   869 my ($self, $url) = @_;
257              
258 328 100       1734 if ($url =~ m!^https?://!) {
259 289         3132 warn "[JSON::Validator] Loading schema from URL $url\n" if DEBUG;
260 289         974 return $self->_load_schema_from_url(Mojo::URL->new($url)->fragment(undef)),
261             "$url";
262             }
263              
264 39 100       322 if ($url =~ m!^data://([^/]*)/(.*)!) {
265 18         188 my ($class, $file) = ($1, $2);
266 18         88 my $text = data_section $class, $file, {confess => 1, encoding => 'UTF-8'};
267 17         1405 return $self->_load_schema_from_text(\$text), "$url";
268             }
269              
270 21 50       92 if ($url =~ m!^\s*[\[\{]!) {
271 0         0 warn "[JSON::Validator] Loading schema from string.\n" if DEBUG;
272 0         0 return $self->_load_schema_from_text(\$url), '';
273             }
274              
275 21         114 my $file = $url;
276 21         40 $file =~ s!^file://!!;
277 21         88 $file =~ s!#$!!;
278 21         87 $file = path(split '/', $file);
279 21 100 33     530 if (-e $file) {
    50          
280 20         434 $file = $file->realpath;
281 20         1019 warn "[JSON::Validator] Loading schema from file: $file\n" if DEBUG;
282 20         56 return $self->_load_schema_from_text(\$file->slurp),
283             CASE_TOLERANT ? path(lc $file) : $file;
284             }
285             elsif ($url =~ m!^/! and $self->ua->server->app) {
286 0         0 warn "[JSON::Validator] Loading schema from URL $url\n" if DEBUG;
287 0         0 return $self->_load_schema_from_url(Mojo::URL->new($url)->fragment(undef)),
288             "$url";
289             }
290              
291 1         40 confess "Unable to load schema '$url' ($file)";
292             }
293              
294             sub _load_schema_from_text {
295 327     327   32437 my ($self, $text) = @_;
296 327         769 my $visit;
297              
298             # JSON
299 327 50       3155 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 48     48   315 no warnings 'once';
  48         1228  
  48         258222  
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   43927 my ($self, $url) = @_;
312 292         1090 my $cache_path = $self->cache_paths->[0];
313 292         945 my $cache_file = Mojo::Util::md5_sum("$url");
314 292         53351 my ($err, $tx);
315              
316 292         552 for (@{$self->cache_paths}) {
  292         830  
317 288         2438 my $path = path $_, $cache_file;
318 288         9376 warn "[JSON::Validator] Looking for cached spec $path ($url)\n" if DEBUG;
319 288 100       1101 next unless -r $path;
320 275         13107 return $self->_load_schema_from_text(\$path->slurp);
321             }
322              
323 17         644 $tx = $self->ua->get($url);
324 17   66     202844 $err = $tx->error && $tx->error->{message};
325 17         334 confess "GET $url == $err" if DEBUG and $err;
326 17 100       64 die "[JSON::Validator] GET $url == $err" if $err;
327              
328 15 50 33     62 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         247 return $self->_load_schema_from_text(\$tx->res->body);
340             }
341              
342             sub _node {
343 108     108   149 my ($node, $path, $offset, $create) = @_;
344              
345 108         127 my $n = 0;
346 108         157 while ($path->[$n]) {
347 139 100 100     313 $node->{$path->[$n]} ||= {} if $create;
348 139 100       281 return undef unless $node = $node->{$path->[$n]};
349 119 100       207 last if (++$n) + $offset >= @$path;
350             }
351              
352 88         180 return $node;
353             }
354              
355             sub _ref_to_schema {
356 1456     1456   2964 my ($self, $schema) = @_;
357              
358 1456         1946 my @guard;
359 1456         3539 while (my $tied = tied %$schema) {
360 1456         3612 push @guard, $tied->ref;
361 1456 50       3616 confess "Seems like you have a circular reference: @guard"
362             if @guard > RECURSION_LIMIT;
363 1456         3296 $schema = $tied->schema;
364 1456 100       3185 last if is_type $schema, 'BOOL';
365             }
366              
367 1456         3225 return $schema;
368             }
369              
370             sub _register_schema {
371 1191     1191   58223 my ($self, $schema, $fqn) = @_;
372 1191         4789 $fqn =~ s!(.)#$!$1!;
373 1191         4370 $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 7863     7863   15463 my ($self, $schema) = @_;
380 7863         18236 my $id_key = $self->_id_key;
381 7863         48044 my ($id, $resolved, @refs);
382              
383 7863   100     27120 local $self->{level} = $self->{level} || 0;
384 7863 100       20062 delete $_[0]->{schemas}{''} unless $self->{level};
385              
386 7863 100 50     29496 if (ref $schema eq 'HASH') {
    100          
    100          
387 574   100     2593 $id = $schema->{$id_key} // '';
388 574 50       1550 return $resolved if $resolved = $self->{schemas}{$id};
389             }
390             elsif ($resolved = $self->{schemas}{$schema // ''}) {
391 6953         1463908 return $resolved;
392             }
393             elsif (is_type $schema, 'BOOL') {
394 8         24 $self->_register_schema($schema, $schema);
395 8         52 return $schema;
396             }
397             else {
398 328         4345 ($schema, $id) = $self->_load_schema($schema);
399 324 100       1233930 $id = $schema->{$id_key} if $schema->{$id_key};
400             }
401              
402 898 100       2504 unless ($self->{level}) {
403 875   100     3191 my $rid = $schema->{$id_key} // $id;
404 875 100       1893 if ($rid) {
405 303 100       1323 confess "Root schema cannot have a fragment in the 'id'. ($rid)"
406             if $rid =~ /\#./;
407 302 100 100     2147 confess "Root schema cannot have a relative 'id'. ($rid)"
      66        
408             unless $rid =~ /^\w+:/
409             or -e $rid
410             or $rid =~ m!^/!;
411             }
412 872         1459 warn sprintf "[JSON::Validator] Using root_schema_url of '$rid'\n" if DEBUG;
413 872         1836 $self->{root_schema_url} = $rid;
414             }
415              
416 895         1464 $self->{level}++;
417 895         3232 $self->_register_schema($schema, $id);
418              
419 895         1686 my %seen;
420             my @topics
421 895 100       2734 = ([$schema, is_type($id, 'Mojo::File') ? $id : Mojo::URL->new($id)]);
422 895         135908 while (@topics) {
423 39670         57815 my ($topic, $base) = @{shift @topics};
  39670         72898  
424              
425 39670 100       91674 if (is_type $topic, 'ARRAY') {
    100          
426 2750         4860 push @topics, map { [$_, $base] } @$topic;
  6285         15900  
427             }
428             elsif (is_type $topic, 'HASH') {
429 20506         64373 my $seen_addr = join ':', $base, refaddr($topic);
430 20506 50       3650015 next if $seen{$seen_addr}++;
431              
432             push @refs, [$topic, $base] and next
433 20506 100 50     83930 if $topic->{'$ref'} and !ref $topic->{'$ref'};
      100        
434              
435 13624 100 100     30098 if ($topic->{$id_key} and !ref $topic->{$id_key}) {
436 288         1090 my $fqn = Mojo::URL->new($topic->{$id_key});
437 288 100       30700 $fqn = $fqn->to_abs($base) unless $fqn->is_abs;
438 288         7174 $self->_register_schema($topic, $fqn->to_string);
439             }
440              
441 13624         41381 push @topics, map { [$_, $base] } values %$topic;
  32490         84488  
442             }
443             }
444              
445             # Need to register "id":"..." before resolving "$ref":"..."
446 895         3549 $self->_resolve_ref(@$_) for @refs;
447              
448 891         13983 return $schema;
449             }
450              
451             sub _location_to_abs {
452 6965     6965   12449 my ($location, $base) = @_;
453 6965         18026 my $location_as_url = Mojo::URL->new($location);
454 6965 100       1132584 return $location_as_url if $location_as_url->is_abs;
455              
456             # definitely relative now
457 6836 100       51815 if (is_type $base, 'Mojo::File') {
458 22 100       80 return $base if !length $location;
459 15         70 my $path = $base->sibling(split '/', $location)->realpath;
460 15         1793 return CASE_TOLERANT ? lc($path) : $path;
461             }
462 6814         18421 return $location_as_url->to_abs($base);
463             }
464              
465             sub _resolve_ref {
466 6882     6882   15217 my ($self, $topic, $url) = @_;
467 6882 100       15502 return if tied %$topic;
468              
469 6881         10698 my $other = $topic;
470 6881         10007 my ($fqn, $ref, @guard);
471              
472 6881         8829 while (1) {
473 13842 100       33100 last if is_type $other, 'BOOL';
474 13831         29466 $ref = $other->{'$ref'};
475 13831         23307 push @guard, $other->{'$ref'};
476 13831 50       26043 confess "Seems like you have a circular reference: @guard"
477             if @guard > RECURSION_LIMIT;
478 13831 100 66     41578 last if !$ref or ref $ref;
479 6965 50       17283 $fqn = $ref =~ m!^/! ? "#$ref" : $ref;
480 6965         22765 my ($location, $pointer) = split /#/, $fqn, 2;
481 6965         14767 $url = $location = _location_to_abs($location, $url);
482 6965 100 100     1833605 $pointer = undef if length $location and !length $pointer;
483 6965 100       1529782 $pointer = url_unescape $pointer if defined $pointer;
484 6965         47842 $fqn = join '#', grep defined, $location, $pointer;
485 6965         1438621 $other = $self->_resolve($location);
486              
487 6962 100 100     41882 if (defined $pointer and length $pointer and $pointer =~ m!^/!) {
      100        
488 4472         16772 $other = Mojo::JSON::Pointer->new($other)->get($pointer);
489 4472 100       189959 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         29879 tie %$topic, 'JSON::Validator::Ref', $other, $topic->{'$ref'}, $fqn;
496             }
497              
498             sub _validate {
499 4898     4898   10954 my ($self, $data, $path, $schema) = @_;
500 4898         7677 my ($seen_addr, $to_json, $type);
501              
502             $schema = $self->_ref_to_schema($schema)
503 4898 100 100     19631 if ref $schema eq 'HASH' and $schema->{'$ref'};
504 4898 100       11356 return $schema ? () : E $path, [not => 'not'] if is_type $schema, 'BOOL';
    100          
505              
506             $seen_addr = join ':', refaddr($schema),
507 4824 100       20765 (ref $data ? refaddr $data : ++$self->{seen}{scalar});
508              
509             # Avoid recursion
510 4824 100       11118 if ($self->{seen}{$seen_addr}) {
511 293         378 return @{$self->{seen}{$seen_addr}};
  293         712  
512             }
513              
514 4531         11259 $self->{seen}{$seen_addr} = \my @errors;
515 4531 100 100     13324 $to_json
516             = (blessed $data and $data->can('TO_JSON')) ? \$data->TO_JSON : undef;
517 4531 100       8836 $data = $$to_json if $to_json;
518 4531   100     12147 $type = $schema->{type} || schema_type $schema, $data;
519              
520             # Test base schema before allOf, anyOf or oneOf
521 4531 100       11271 if (ref $type eq 'ARRAY') {
    100          
522 97         124 push @{$self->{temp_schema}}, [map { +{%$schema, type => $_} } @$type];
  97         539  
  193         1174  
523             push @errors,
524             $self->_validate_any_of($to_json ? $$to_json : $_[1],
525 97 50       337 $path, $self->{temp_schema}[-1]);
526             }
527             elsif ($type) {
528 2771         9605 my $method = sprintf '_validate_type_%s', $type;
529 2771 100       12536 @errors = $self->$method($to_json ? $$to_json : $_[1], $path, $schema);
530 2771 100       6546 return @errors if @errors;
531             }
532              
533 4008 100       7493 if (exists $schema->{const}) {
534 32 50       113 push @errors,
535             $self->_validate_type_const($to_json ? $$to_json : $_[1], $path, $schema);
536 32 100       876 return @errors if @errors;
537             }
538              
539 3996 100       7494 if ($schema->{enum}) {
540 505 50       1659 push @errors,
541             $self->_validate_type_enum($to_json ? $$to_json : $_[1], $path, $schema);
542 505 100       19639 return @errors if @errors;
543             }
544              
545 3974 100       7622 if (my $rules = $schema->{not}) {
546 11 50       64 push @errors, $self->_validate($to_json ? $$to_json : $_[1], $path, $rules);
547 11 100       60 return @errors ? () : (E $path, [not => 'not']);
548             }
549              
550 3963 100       10854 if (my $rules = $schema->{allOf}) {
    100          
    100          
551 107 50       445 push @errors,
552             $self->_validate_all_of($to_json ? $$to_json : $_[1], $path, $rules);
553             }
554             elsif ($rules = $schema->{anyOf}) {
555 565 50       2005 push @errors,
556             $self->_validate_any_of($to_json ? $$to_json : $_[1], $path, $rules);
557             }
558             elsif ($rules = $schema->{oneOf}) {
559 28 50       107 push @errors,
560             $self->_validate_one_of($to_json ? $$to_json : $_[1], $path, $rules);
561             }
562              
563 3963         8799 return @errors;
564             }
565              
566             sub _validate_all_of {
567 107     107   298 my ($self, $data, $path, $rules) = @_;
568 107         304 my $type = data_type $data, $rules;
569 107         237 my (@errors, @expected);
570              
571 107         194 my $i = 0;
572 107         274 for my $rule (@$rules) {
573 207 100       565 next unless my @e = $self->_validate($_[1], $path, $rule);
574 18         45 my $schema_type = schema_type $rule;
575 18 100       57 push @expected, $schema_type if $schema_type;
576 18 100 100     99 push @errors, [$i, @e] if !$schema_type or $schema_type eq $type;
577             }
578             continue {
579 207         410 $i++;
580             }
581              
582 107 100 100     576 return E $path, [allOf => type => join('/', uniq @expected), $type]
583             if !@errors and @expected;
584 106 100       329 return prefix_errors allOf => @errors if @errors;
585 93         197 return;
586             }
587              
588             sub _validate_any_of {
589 662     662   1727 my ($self, $data, $path, $rules) = @_;
590 662         1689 my $type = data_type $data, $rules;
591 662         1525 my (@e, @errors, @expected);
592              
593 662         965 my $i = 0;
594 662         1380 for my $rule (@$rules) {
595 774         2109 @e = $self->_validate($_[1], $path, $rule);
596 774 100       2414 return unless @e;
597 127         386 my $schema_type = schema_type $rule;
598 127 100 50     654 push @errors, [$i, @e] and next if !$schema_type or $schema_type eq $type;
      100        
599 103         257 push @expected, $schema_type;
600             }
601             continue {
602 127         307 $i++;
603             }
604              
605 15         47 my $expected = join '/', uniq @expected;
606 15 100       102 return E $path, [anyOf => type => $expected, $type] unless @errors;
607 8         39 return prefix_errors anyOf => @errors;
608             }
609              
610             sub _validate_one_of {
611 28     28   75 my ($self, $data, $path, $rules) = @_;
612 28         69 my $type = data_type $data, $rules;
613 28         101 my (@errors, @expected);
614              
615 28         58 my ($i, @passed) = (0);
616 28         69 for my $rule (@$rules) {
617 59 100 50     146 my @e = $self->_validate($_[1], $path, $rule) or push @passed, $i and next;
618 31         79 my $schema_type = schema_type $rule;
619 31 100 50     174 push @errors, [$i, @e] and next if !$schema_type or $schema_type eq $type;
      100        
620 19         43 push @expected, $schema_type;
621             }
622             continue {
623 59         109 $i++;
624             }
625              
626 28 100       83 return if @passed == 1;
627 10 100       50 return E $path, [oneOf => 'all_rules_match'] unless @errors + @expected;
628 7 100       22 return E $path, [oneOf => 'n_rules_match', join(', ', @passed)] if @passed;
629 5 100       20 return prefix_errors oneOf => @errors if @errors;
630 1         4 return E $path, [oneOf => type => join('/', uniq @expected), $type];
631             }
632              
633             sub _validate_number_max {
634 379     379   1011 my ($self, $value, $path, $schema, $expected) = @_;
635 379         852 my @errors;
636              
637 379   100     1305 my $cmp_with = $schema->{exclusiveMaximum} // '';
638 379 100       901 if (is_type $cmp_with, 'BOOL') {
    100          
639             push @errors, E $path,
640             [$expected => ex_maximum => $value, $schema->{maximum}]
641 4 100       25 unless $value < $schema->{maximum};
642             }
643             elsif (is_type $cmp_with, 'NUM') {
644 2 100       8 push @errors, E $path, [$expected => ex_maximum => $value, $cmp_with]
645             unless $value < $cmp_with;
646             }
647              
648 379 100       1043 if (exists $schema->{maximum}) {
649 32         66 my $cmp_with = $schema->{maximum};
650 32 100       111 push @errors, E $path, [$expected => maximum => $value, $cmp_with]
651             unless $value <= $cmp_with;
652             }
653              
654 379         860 return @errors;
655             }
656              
657             sub _validate_number_min {
658 379     379   966 my ($self, $value, $path, $schema, $expected) = @_;
659 379         507 my @errors;
660              
661 379   100     2721 my $cmp_with = $schema->{exclusiveMinimum} // '';
662 379 100       893 if (is_type $cmp_with, 'BOOL') {
    100          
663             push @errors, E $path,
664             [$expected => ex_minimum => $value, $schema->{minimum}]
665 12 100       95 unless $value > $schema->{minimum};
666             }
667             elsif (is_type $cmp_with, 'NUM') {
668 2 100       8 push @errors, E $path, [$expected => ex_minimum => $value, $cmp_with]
669             unless $value > $cmp_with;
670             }
671              
672 379 100       1040 if (exists $schema->{minimum}) {
673 158         350 my $cmp_with = $schema->{minimum};
674 158 100       484 push @errors, E $path, [$expected => minimum => $value, $cmp_with]
675             unless $value >= $cmp_with;
676             }
677              
678 379         988 return @errors;
679             }
680              
681             sub _validate_type_enum {
682 524     524   1279 my ($self, $data, $path, $schema) = @_;
683 524         903 my $enum = $schema->{enum};
684 524         1380 my $m = data_checksum $data;
685              
686 524         37397 for my $i (@$enum) {
687 1979 100       57788 return if $m eq data_checksum $i;
688             }
689              
690             $enum = join ', ',
691 22 100 66     835 map { (!defined or ref) ? Mojo::JSON::encode_json($_) : $_ } @$enum;
  112         660  
692 22         320 return E $path, [enum => enum => $enum];
693             }
694              
695             sub _validate_type_const {
696 51     51   144 my ($self, $data, $path, $schema) = @_;
697 51         109 my $const = $schema->{const};
698              
699 51 100       129 return if data_checksum($data) eq data_checksum($const);
700 20         872 return E $path, [const => const => Mojo::JSON::encode_json($const)];
701             }
702              
703             sub _validate_format {
704 233     233   455 my ($self, $value, $path, $schema) = @_;
705 233         657 my $code = $self->formats->{$schema->{format}};
706 233 100       1409 return do { warn "Format rule for '$schema->{format}' is missing"; return }
  1         16  
  1         5  
707             unless $code;
708 232 100       708 return unless my $err = $code->($value);
709 37         189 return E $path, [format => $schema->{format}, $err];
710             }
711              
712       8     sub _validate_type_any { }
713              
714             sub _validate_type_array {
715 262     262   683 my ($self, $data, $path, $schema) = @_;
716 262         363 my @errors;
717              
718 262 100       759 if (ref $data ne 'ARRAY') {
719 34         82 return E $path, [array => type => data_type $data];
720             }
721 228 100 100     1172 if (defined $schema->{minItems} and $schema->{minItems} > @$data) {
722             push @errors, E $path,
723 4         25 [array => minItems => int(@$data), $schema->{minItems}];
724             }
725 228 100 100     737 if (defined $schema->{maxItems} and $schema->{maxItems} < @$data) {
726             push @errors, E $path,
727 3         19 [array => maxItems => int(@$data), $schema->{maxItems}];
728             }
729 228 100       823 if ($schema->{uniqueItems}) {
730 93         851 my %uniq;
731 93         255 for (@$data) {
732 177 100       5382 next if !$uniq{data_checksum($_)}++;
733 7         307 push @errors, E $path, [array => 'uniqueItems'];
734 7         19 last;
735             }
736             }
737              
738 228 100       6070 if ($schema->{contains}) {
    100          
    100          
739 2         4 my @e;
740 2         6 for my $i (0 .. @$data - 1) {
741 4         13 my @tmp = $self->_validate($data->[$i], "$path/$i", $schema->{contains});
742 4 100       13 push @e, \@tmp if @tmp;
743             }
744 2 100       7 push @errors, map {@$_} @e if @e >= @$data;
  2         3  
745             }
746             elsif (ref $schema->{items} eq 'ARRAY') {
747 17   100     99 my $additional_items = $schema->{additionalItems} // {type => 'any'};
748 17         40 my @rules = @{$schema->{items}};
  17         42  
749              
750 17 100       44 if ($additional_items) {
751 14         49 push @rules, $additional_items while @rules < @$data;
752             }
753              
754 17 100       65 if (@rules == @$data) {
    100          
755 13         37 for my $i (0 .. @rules - 1) {
756 38         166 push @errors, $self->_validate($data->[$i], "$path/$i", $rules[$i]);
757             }
758             }
759             elsif (!$additional_items) {
760 2         23 push @errors, E $path,
761             [array => additionalItems => int(@$data), int(@rules)];
762             }
763             }
764             elsif (exists $schema->{items}) {
765 156         649 for my $i (0 .. @$data - 1) {
766 269         1389 push @errors, $self->_validate($data->[$i], "$path/$i", $schema->{items});
767             }
768             }
769              
770 228         518 return @errors;
771             }
772              
773             sub _validate_type_boolean {
774 146     146   360 my ($self, $value, $path, $schema) = @_;
775 146 100       360 return if is_type $value, 'BOOL';
776              
777             # String that looks like a boolean
778 81 100 100     550 if (
      100        
      100        
779             defined $value
780             and $self->{coerce}{booleans}
781             and (B::svref_2object(\$value)->FLAGS & (B::SVp_IOK | B::SVp_NOK)
782             or $value =~ /^(true|false)$/)
783             )
784             {
785 27 100       88 $_[1] = $value ? true : false;
786 27         146 return;
787             }
788              
789 54         141 return E $path, [boolean => type => data_type $value];
790             }
791              
792             sub _validate_type_integer {
793 254     254   656 my ($self, $value, $path, $schema) = @_;
794 254         911 my @errors = $self->_validate_type_number($_[1], $path, $schema, 'integer');
795              
796 254 100       616 return @errors if @errors;
797 209 100       1358 return if $value =~ /^-?\d+$/;
798 6         25 return E $path, [integer => type => data_type $value];
799             }
800              
801             sub _validate_type_null {
802 14     14   48 my ($self, $value, $path, $schema) = @_;
803              
804 14 100       82 return unless defined $value;
805 8         42 return E $path, ['null', 'null'];
806             }
807              
808             sub _validate_type_number {
809 431     431   1128 my ($self, $value, $path, $schema, $expected) = @_;
810 431         626 my @errors;
811              
812 431   100     1330 $expected ||= 'number';
813              
814 431 100 100     1652 if (!defined $value or ref $value) {
815 17         61 return E $path, [$expected => type => data_type $value];
816             }
817 414 100       975 unless (is_type $value, 'NUM') {
818             return E $path, [$expected => type => data_type $value]
819             if !$self->{coerce}{numbers}
820 85 100 100     688 or $value !~ /^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$/;
821 50         137 $_[1] = 0 + $value; # coerce input value
822             }
823              
824             push @errors, $self->_validate_format($value, $path, $schema)
825 379 50       1153 if $schema->{format};
826 379         1268 push @errors, $self->_validate_number_max($value, $path, $schema, $expected);
827 379         1075 push @errors, $self->_validate_number_min($value, $path, $schema, $expected);
828              
829 379         742 my $d = $schema->{multipleOf};
830 379 100 100     1141 push @errors, E $path, [$expected => multipleOf => $d]
831             if $d and ($value / $d) =~ /\.[^0]+$/;
832              
833 379         731 return @errors;
834             }
835              
836             sub _validate_type_object {
837 1397     1397   3278 my ($self, $data, $path, $schema) = @_;
838              
839 1397 100       3653 if (ref $data ne 'HASH') {
840 44         144 return E $path, [object => type => data_type $data];
841             }
842              
843 1353         2018 my @errors;
844 1353         6220 my @dkeys = sort keys %$data;
845 1353 100 100     4469 if (defined $schema->{maxProperties} and $schema->{maxProperties} < @dkeys) {
846             push @errors, E $path,
847 2         14 [object => maxProperties => int(@dkeys), $schema->{maxProperties}];
848             }
849 1353 100 100     3721 if (defined $schema->{minProperties} and $schema->{minProperties} > @dkeys) {
850             push @errors, E $path,
851 3         18 [object => minProperties => int(@dkeys), $schema->{minProperties}];
852             }
853 1353 100       3292 if (my $n_schema = $schema->{propertyNames}) {
854 3         9 for my $name (keys %$data) {
855 4 100       8 next unless my @e = $self->_validate($name, $path, $n_schema);
856 1         3 push @errors, prefix_errors propertyName => [map { ($name, $_) } @e];
  1         6  
857             }
858             }
859 1353 100       2855 if ($schema->{if}) {
860             push @errors,
861             $self->_validate($data, $path, $schema->{if})
862             ? $self->_validate($data, $path, $schema->{else} // {})
863 4 100 50     12 : $self->_validate($data, $path, $schema->{then} // {});
      50        
864             }
865              
866 1353         2012 my %rules;
867 1353         1843 for my $k (keys %{$schema->{properties}}) {
  1353         10981  
868 28061         45002 my $r = $schema->{properties}{$k};
869 28061         32153 push @{$rules{$k}}, $r;
  28061         53728  
870 28061 100 100     57212 if ( $self->{coerce}{defaults}
      100        
      100        
871             and ref $r eq 'HASH'
872             and exists $r->{default}
873             and !exists $data->{$k})
874             {
875 18         51 $data->{$k} = $r->{default};
876             }
877             }
878              
879 1353 100       3219 for my $p (keys %{$schema->{patternProperties} || {}}) {
  1353         6745  
880 53         142 my $r = $schema->{patternProperties}{$p};
881 53         117 push @{$rules{$_}}, $r for sort grep { $_ =~ /$p/ } @dkeys;
  81         753  
  37         149  
882             }
883              
884             my $additional
885             = exists $schema->{additionalProperties}
886             ? $schema->{additionalProperties}
887 1353 100       4484 : {};
888 1353 100       2749 if ($additional) {
    100          
889 1341 100       3541 $additional = {} unless is_type $additional, 'HASH';
890 1341   100     6627 $rules{$_} ||= [$additional] for @dkeys;
891             }
892 20         157 elsif (my @k = grep { !$rules{$_} } @dkeys) {
893 6         25 local $" = ', ';
894 6         41 return E $path, [object => additionalProperties => join '/', @k];
895             }
896              
897 1347 100       2064 for my $k (sort uniq @{$schema->{required} || []}) {
  1347         6785  
898 96 100       282 next if exists $data->{$k};
899 24         107 push @errors, E json_pointer($path, $k), [object => 'required'];
900 24         83 delete $rules{$k};
901             }
902              
903 1347         14347 for my $k (sort keys %rules) {
904 28755         33849 for my $r (@{$rules{$k}}) {
  28755         48331  
905 28763 100       60620 next unless exists $data->{$k};
906 2608 100 100     10731 $r = $self->_ref_to_schema($r) if ref $r eq 'HASH' and $r->{'$ref'};
907 2608         8234 my @e = $self->_validate($data->{$k}, json_pointer($path, $k), $r);
908 2608         4672 push @errors, @e;
909 2608 100 100     8107 next if @e or !is_type $r, 'HASH';
910             push @errors,
911             $self->_validate_type_enum($data->{$k}, json_pointer($path, $k), $r)
912 2448 100       5406 if $r->{enum};
913             push @errors,
914             $self->_validate_type_const($data->{$k}, json_pointer($path, $k), $r)
915 2448 100       6828 if $r->{const};
916             }
917             }
918              
919 1347         10039 return @errors;
920             }
921              
922             sub _validate_type_string {
923 497     497   1283 my ($self, $value, $path, $schema) = @_;
924 497         743 my @errors;
925              
926 497 100 100     1760 if (!defined $value or ref $value) {
927 16         56 return E $path, [string => type => data_type $value];
928             }
929 481 50 66     2971 if ( B::svref_2object(\$value)->FLAGS & (B::SVp_IOK | B::SVp_NOK)
      66        
930             and 0 + $value eq $value
931             and $value * 0 == 0)
932             {
933             return E $path, [string => type => data_type $value]
934 22 100       137 unless $self->{coerce}{strings};
935 1         3 $_[1] = "$value"; # coerce input value
936             }
937 460 100       1251 if ($schema->{format}) {
938 233         604 push @errors, $self->_validate_format($value, $path, $schema);
939             }
940 460 100       1090 if (defined $schema->{maxLength}) {
941 30 100       104 if (length($value) > $schema->{maxLength}) {
942             push @errors, E $path,
943 11         50 [string => maxLength => length($value), $schema->{maxLength}];
944             }
945             }
946 460 100       947 if (defined $schema->{minLength}) {
947 24 100       72 if (length($value) < $schema->{minLength}) {
948             push @errors, E $path,
949 4         33 [string => minLength => length($value), $schema->{minLength}];
950             }
951             }
952 460 100       934 if (defined $schema->{pattern}) {
953 9         17 my $p = $schema->{pattern};
954 9 100       160 push @errors, E $path, [string => pattern => $p] unless $value =~ /$p/;
955             }
956              
957 460         1012 return @errors;
958             }
959              
960             1;
961              
962             =encoding utf8
963              
964             =head1 NAME
965              
966             JSON::Validator - Validate data against a JSON schema
967              
968             =head1 SYNOPSIS
969              
970             use JSON::Validator;
971             my $jv = JSON::Validator->new;
972              
973             # Define a schema - http://json-schema.org/learn/miscellaneous-examples.html
974             # You can also load schema from disk or web
975             $jv->schema({
976             type => "object",
977             required => ["firstName", "lastName"],
978             properties => {
979             firstName => {type => "string"},
980             lastName => {type => "string"},
981             age => {type => "integer", minimum => 0, description => "Age in years"}
982             }
983             });
984              
985             # Validate your data
986             my @errors = $jv->validate({firstName => "Jan Henning", lastName => "Thorsen", age => -42});
987              
988             # Do something if any errors was found
989             die "@errors" if @errors;
990              
991             # Use joi() to build the schema
992             use JSON::Validator 'joi';
993              
994             $jv->schema(joi->object->props({
995             firstName => joi->string->required,
996             lastName => joi->string->required,
997             age => joi->integer->min(0),
998             }));
999              
1000             # joi() can also validate directly
1001             my @errors = joi(
1002             {firstName => "Jan Henning", lastName => "Thorsen", age => -42},
1003             joi->object->props({
1004             firstName => joi->string->required,
1005             lastName => joi->string->required,
1006             age => joi->integer->min(0),
1007             });
1008             );
1009              
1010             =head1 DESCRIPTION
1011              
1012             L is a data structure validation library based around
1013             L. This module can be used directly with
1014             a JSON schema or you can use the elegant DSL schema-builder
1015             L to define the schema programmatically.
1016              
1017             =head2 Supported schema formats
1018              
1019             L can load JSON schemas in multiple formats: Plain perl data
1020             structured (as shown in L), JSON or YAML. The JSON parsing is done
1021             with L, while YAML files require the optional module L to
1022             be installed.
1023              
1024             =head2 Resources
1025              
1026             Here are some resources that are related to JSON schemas and validation:
1027              
1028             =over 4
1029              
1030             =item * L
1031              
1032             =item * L
1033              
1034             =item * L
1035              
1036             =back
1037              
1038             =head2 Bundled specifications
1039              
1040             This module comes with some JSON specifications bundled, so your application
1041             don't have to fetch those from the web. These specifications should be up to
1042             date, but please submit an issue if they are not.
1043              
1044             Files referenced to an URL will automatically be cached if the first element in
1045             L is a writable directory. Note that the cache headers for the
1046             remote assets are B honored, so you will manually need to remove any
1047             cached file, should you need to refresh them.
1048              
1049             To download and cache an online asset, do this:
1050              
1051             JSON_VALIDATOR_CACHE_PATH=/some/writable/directory perl myapp.pl
1052              
1053             Here is the list of the bundled specifications:
1054              
1055             =over 2
1056              
1057             =item * JSON schema, draft 4, 6, 7
1058              
1059             Web page: L
1060              
1061             C<$ref>: L,
1062             L,
1063             L.
1064              
1065             =item * JSON schema for JSONPatch files
1066              
1067             Web page: L
1068              
1069             C<$ref>: L
1070              
1071             =item * Swagger / OpenAPI specification, version 2
1072              
1073             Web page: L
1074              
1075             C<$ref>: L
1076              
1077             =item * OpenAPI specification, version 3
1078              
1079             Web page: L
1080              
1081             C<$ref>: L
1082              
1083             This specification is still EXPERIMENTAL.
1084              
1085             =item * Swagger Petstore
1086              
1087             This is used for unit tests, and should not be relied on by external users.
1088              
1089             =back
1090              
1091             =head1 ERROR OBJECT
1092              
1093             The methods L and the function L returns a list of
1094             L objects when the input data violates the L.
1095              
1096             =head1 FUNCTIONS
1097              
1098             =head2 joi
1099              
1100             use JSON::Validator "joi";
1101             my $joi = joi;
1102             my @errors = joi($data, $joi); # same as $joi->validate($data);
1103              
1104             Used to construct a new L object or perform validation.
1105              
1106             =head2 validate_json
1107              
1108             use JSON::Validator "validate_json";
1109             my @errors = validate_json $data, $schema;
1110              
1111             This can be useful in web applications:
1112              
1113             my @errors = validate_json $c->req->json, "data://main/spec.json";
1114              
1115             See also L and L for more details.
1116              
1117             =head1 ATTRIBUTES
1118              
1119             =head2 cache_paths
1120              
1121             my $jv = $jv->cache_paths(\@paths);
1122             my $array_ref = $jv->cache_paths;
1123              
1124             A list of directories to where cached specifications are stored. Defaults to
1125             C environment variable and the specs that is bundled
1126             with this distribution.
1127              
1128             C can be a list of directories, each separated by ":".
1129              
1130             See L for more details.
1131              
1132             =head2 formats
1133              
1134             my $hash_ref = $jv->formats;
1135             my $jv = $jv->formats(\%hash);
1136              
1137             Holds a hash-ref, where the keys are supported JSON type "formats", and
1138             the values holds a code block which can validate a given format. A code
1139             block should return C on success and an error string on error:
1140              
1141             sub { return defined $_[0] && $_[0] eq "42" ? undef : "Not the answer." };
1142              
1143             See L for a list of supported formats.
1144              
1145             =head2 generate_definitions_path
1146              
1147             my $cb = $jv->generate_definitions_path;
1148             my $jv = $jv->generate_definitions_path(sub { my $ref = shift; return ["definitions"] });
1149              
1150             Holds a callback that is used by L to figure out where to place
1151             references. The default location is under "definitions", but this can be
1152             changed to whatever you want. The input C<$ref> variable passed on is a
1153             L object.
1154              
1155             This attribute is EXPERIMENTAL and might change without warning.
1156              
1157             =head2 ua
1158              
1159             my $ua = $jv->ua;
1160             my $jv = $jv->ua(Mojo::UserAgent->new);
1161              
1162             Holds a L object, used by L to load a JSON schema
1163             from remote location.
1164              
1165             The default L will detect proxy settings and have
1166             L set to 3.
1167              
1168             =head2 version
1169              
1170             my $int = $jv->version;
1171             my $jv = $jv->version(7);
1172              
1173             Used to set the JSON Schema version to use. Will be set automatically when
1174             using L, unless already set.
1175              
1176             =head1 METHODS
1177              
1178             =head2 bundle
1179              
1180             # These two lines does the same
1181             my $schema = $jv->bundle({schema => $jv->schema->data});
1182             my $schema = $jv->bundle;
1183              
1184             # Will only bundle a section of the schema
1185             my $schema = $jv->bundle({schema => $jv->schema->get("/properties/person/age")});
1186              
1187             Used to create a new schema, where there are no "$ref" pointing to external
1188             resources. This means that all the "$ref" that are found, will be moved into
1189             the "definitions" key, in the returned C<$schema>.
1190              
1191             =head2 coerce
1192              
1193             my $jv = $jv->coerce('bool,def,num,str');
1194             my $jv = $jv->coerce('booleans,defaults,numbers,strings');
1195             my $hash_ref = $jv->coerce;
1196              
1197             Set the given type to coerce. Before enabling coercion this module is very
1198             strict when it comes to validating types. Example: The string C<"1"> is not
1199             the same as the number C<1>, unless you have "numbers" coercion enabled.
1200              
1201             =over 2
1202              
1203             =item * booleans
1204              
1205             Will convert what looks can be interpreted as a boolean (that is, an actual
1206             numeric C<1> or C<0>, and the strings "true" and "false") to a
1207             L object. Note that "foo" is not considered a true value and
1208             will fail the validation.
1209              
1210             =item * defaults
1211              
1212             Will copy the default value defined in the schema, into the input structure,
1213             if the input value is non-existing.
1214              
1215             Note that support for "default" is currently EXPERIMENTAL, and enabling this
1216             might be changed in future versions.
1217              
1218             =item * numbers
1219              
1220             Will convert strings that looks like numbers, into true numbers. This works for
1221             both the "integer" and "number" types.
1222              
1223             =item * strings
1224              
1225             Will convert a number into a string. This works for the "string" type.
1226              
1227             =back
1228              
1229             =head2 get
1230              
1231             my $sub_schema = $jv->get("/x/y");
1232             my $sub_schema = $jv->get(["x", "y"]);
1233              
1234             Extract value from L identified by the given JSON Pointer. Will at the
1235             same time resolve C<$ref> if found. Example:
1236              
1237             $jv->schema({x => {'$ref' => '#/y'}, y => {'type' => 'string'}});
1238             $jv->schema->get('/x') == {'$ref' => '#/y'}
1239             $jv->schema->get('/x')->{'$ref'} == '#/y'
1240             $jv->get('/x') == {type => 'string'}
1241              
1242             The argument can also be an array-ref with the different parts of the pointer
1243             as each elements.
1244              
1245             =head2 new
1246              
1247             $jv = JSON::Validator->new(%attributes);
1248             $jv = JSON::Validator->new(\%attributes);
1249              
1250             Creates a new L object.
1251              
1252             =head2 load_and_validate_schema
1253              
1254             my $jv = $jv->load_and_validate_schema($schema, \%args);
1255              
1256             Will load and validate C<$schema> against the OpenAPI specification. C<$schema>
1257             can be anything L accepts. The expanded specification
1258             will be stored in L on success. See
1259             L for the different version of C<$url> that can be
1260             accepted.
1261              
1262             C<%args> can be used to further instruct the validation process:
1263              
1264             =over 2
1265              
1266             =item * schema
1267              
1268             Defaults to "http://json-schema.org/draft-04/schema#", but can be any
1269             structured that can be used to validate C<$schema>.
1270              
1271             =back
1272              
1273             =head2 schema
1274              
1275             my $jv = $jv->schema($json_or_yaml_string);
1276             my $jv = $jv->schema($url);
1277             my $jv = $jv->schema(\%schema);
1278             my $jv = $jv->schema(JSON::Validator::Joi->new);
1279             my $schema = $jv->schema;
1280              
1281             Used to set a schema from either a data structure or a URL.
1282              
1283             C<$schema> will be a L object when loaded,
1284             and C by default.
1285              
1286             The C<$url> can take many forms, but needs to point to a text file in the
1287             JSON or YAML format.
1288              
1289             =over 4
1290              
1291             =item * file://...
1292              
1293             A file on disk. Note that it is required to use the "file" scheme if you want
1294             to reference absolute paths on your file system.
1295              
1296             =item * http://... or https://...
1297              
1298             A web resource will be fetched using the L, stored in L.
1299              
1300             =item * data://Some::Module/spec.json
1301              
1302             Will load a given "spec.json" file from C using
1303             L.
1304              
1305             =item * data:///spec.json
1306              
1307             A "data" URL without a module name will use the current package and search up
1308             the call/inheritance tree.
1309              
1310             =item * Any other URL
1311              
1312             An URL (without a recognized scheme) will be treated as a path to a file on
1313             disk.
1314              
1315             =back
1316              
1317             =head2 singleton
1318              
1319             my $jv = JSON::Validator->singleton;
1320              
1321             Returns the L object used by L.
1322              
1323             =head2 validate
1324              
1325             my @errors = $jv->validate($data);
1326             my @errors = $jv->validate($data, $schema);
1327              
1328             Validates C<$data> against a given JSON L. C<@errors> will
1329             contain validation error objects or be an empty list on success.
1330              
1331             See L for details.
1332              
1333             C<$schema> is optional, but when specified, it will override schema stored in
1334             L. Example:
1335              
1336             $jv->validate({hero => "superwoman"}, {type => "object"});
1337              
1338             =head2 SEE ALSO
1339              
1340             =over 2
1341              
1342             =item * L
1343              
1344             L is a plugin for L that utilize
1345             L and the L
1346             to build routes with input and output validation.
1347              
1348             =back
1349              
1350             =head1 COPYRIGHT AND LICENSE
1351              
1352             Copyright (C) 2014-2018, Jan Henning Thorsen
1353              
1354             This program is free software, you can redistribute it and/or modify it under
1355             the terms of the Artistic License version 2.0.
1356              
1357             =head1 AUTHOR
1358              
1359             Jan Henning Thorsen - C
1360              
1361             Daniel Böhmer - C
1362              
1363             Ed J - C
1364              
1365             Karen Etheridge - C
1366              
1367             Kevin Goess - C
1368              
1369             Martin Renvoize - C
1370              
1371             =cut