File Coverage

blib/lib/JSONSchema/Validator/Constraints/Draft4.pm
Criterion Covered Total %
statement 344 377 91.2
branch 140 192 72.9
condition 19 33 57.5
subroutine 41 43 95.3
pod 0 32 0.0
total 544 677 80.3


line stmt bran cond sub pod time code
1             package JSONSchema::Validator::Constraints::Draft4;
2              
3             # ABSTRACT: JSON Schema Draft4 specification constraints
4              
5 6     6   43 use strict;
  6         14  
  6         195  
6 6     6   31 use warnings;
  6         12  
  6         156  
7 6     6   32 use Scalar::Util 'weaken';
  6         10  
  6         223  
8 6     6   32 use URI;
  6         12  
  6         106  
9 6     6   28 use Carp 'croak';
  6         11  
  6         214  
10 6     6   3989 use Data::Dumper;
  6         43713  
  6         364  
11              
12 6     6   45 use JSONSchema::Validator::Error 'error';
  6         14  
  6         232  
13 6     6   35 use JSONSchema::Validator::JSONPointer 'json_pointer';
  6         13  
  6         225  
14 6     6   2751 use JSONSchema::Validator::Util qw(serialize unbool round is_type detect_type);
  6         19  
  6         568  
15 6         2464 use JSONSchema::Validator::Format qw(
16             validate_date_time validate_date validate_time
17             validate_email validate_hostname
18             validate_idn_email
19             validate_ipv4 validate_ipv6
20             validate_uuid
21             validate_byte
22             validate_int32 validate_int64
23             validate_float validate_double
24             validate_regex
25             validate_json_pointer validate_relative_json_pointer
26             validate_uri validate_uri_reference
27             validate_iri validate_iri_reference
28             validate_uri_template
29 6     6   3022 );
  6         26  
30              
31 6         23793 use constant FORMAT_VALIDATIONS => {
32             'date-time' => ['string', \&validate_date_time],
33             'date' => ['string', \&validate_date],
34             'time' => ['string', \&validate_time],
35             'email' => ['string', \&validate_email],
36             'idn-email' => ['string', \&validate_idn_email],
37             'hostname' => ['string', \&validate_hostname],
38             'ipv4' => ['string', \&validate_ipv4],
39             'ipv6' => ['string', \&validate_ipv6],
40             'uuid' => ['string', \&validate_uuid],
41             'byte' => ['string', \&validate_byte],
42             'int32' => ['integer', \&validate_int32],
43             'int64' => ['integer', \&validate_int64],
44             'float' => ['number', \&validate_float],
45             'double' => ['number', \&validate_double],
46             'regex' => ['string', \&validate_regex],
47             'json-pointer' => ['string', \&validate_json_pointer],
48             'relative-json-pointer' => ['string', \&validate_relative_json_pointer],
49             'uri' => ['string', \&validate_uri],
50             'uri-reference' => ['string', \&validate_uri_reference],
51             'iri' => ['string', \&validate_iri],
52             'iri-reference' => ['string', \&validate_iri_reference],
53             'uri-template' => ['string', \&validate_uri_template]
54 6     6   70 };
  6         13  
55              
56             sub new {
57 321     321 0 1254 my ($class, %params) = @_;
58              
59 321 50       1009 my $validator = $params{validator} or croak 'validator is required';
60 321   50     811 my $strict = $params{strict} // 1;
61              
62 321         1045 weaken($validator);
63              
64 321         1126 my $self = {
65             validator => $validator,
66             errors => [],
67             strict => $strict
68             };
69              
70 321         678 bless $self, $class;
71              
72 321         853 return $self;
73             }
74              
75 3467     3467 0 10928 sub validator { shift->{validator} }
76 2993     2993 0 10171 sub strict { shift->{strict} }
77              
78             # params: $self, $value, $type, $strict
79             sub check_type {
80 4285   100 4285 0 12747 return is_type($_[1], $_[2], $_[3] // $_[0]->strict);
81             }
82              
83             sub type {
84 835     835 0 4514 my ($self, $instance, $types, $schema, $instance_path, $schema_path, $data) = @_;
85 835 100       2237 my @types = ref $types ? @$types : ($types);
86              
87 835 100       1536 return 1 if grep { $self->check_type($instance, $_) } @types;
  897         2184  
88              
89 135         276 push @{$data->{errors}}, error(
  135         632  
90             message => "type mismatch",
91             instance_path => $instance_path,
92             schema_path => $schema_path
93             );
94 135         490 return 0;
95             }
96              
97             sub minimum {
98 25     25 0 86 my ($self, $instance, $minimum, $schema, $instance_path, $schema_path, $data) = @_;
99 25 100       123 return 1 unless $self->check_type($instance, 'number');
100 24 100       110 return 1 if $instance >= $minimum;
101 7         19 push @{$data->{errors}}, error(
  7         96  
102             message => "${instance} is less than ${minimum}",
103             instance_path => $instance_path,
104             schema_path => $schema_path
105             );
106 7         25 return 0;
107             }
108              
109             sub maximum {
110 13     13 0 48 my ($self, $instance, $maximum, $schema, $instance_path, $schema_path, $data) = @_;
111 13 50       78 return 1 unless $self->check_type($instance, 'number');
112 13 100       65 return 1 if $instance <= $maximum;
113 3         7 push @{$data->{errors}}, error(
  3         42  
114             message => "${instance} is greater than ${maximum}",
115             instance_path => $instance_path,
116             schema_path => $schema_path
117             );
118 3         12 return 0;
119             }
120              
121             sub exclusiveMaximum {
122 0     0 0 0 my ($self, $instance, $exclusiveMaximum, $schema, $instance_path, $schema_path, $data) = @_;
123 0 0       0 return 1 unless $self->check_type($instance, 'number');
124 0 0       0 return 1 unless exists $schema->{maximum};
125              
126 0         0 my $maximum = $schema->{maximum};
127              
128 0         0 my $res = $self->maximum($instance, $maximum, $schema, $instance_path, $schema_path, $data);
129 0 0       0 return 0 unless $res;
130              
131 0 0       0 return 1 unless $exclusiveMaximum;
132 0 0       0 return 1 if $instance != $maximum;
133              
134 0         0 push @{$data->{errors}}, error(
  0         0  
135             message => "${instance} is equal to ${maximum}",
136             instance_path => $instance_path,
137             schema_path => $schema_path
138             );
139 0         0 return 0;
140             }
141              
142             sub exclusiveMinimum {
143 0     0 0 0 my ($self, $instance, $exclusiveMinimum, $schema, $instance_path, $schema_path, $data) = @_;
144 0 0       0 return 1 unless $self->check_type($instance, 'number');
145 0 0       0 return 1 unless exists $schema->{minimum};
146              
147 0         0 my $minimum = $schema->{minimum};
148              
149 0         0 my $res = $self->minimum($instance, $minimum, $schema, $instance_path, $schema_path, $data);
150 0 0       0 return 0 unless $res;
151              
152 0 0       0 return 1 unless $exclusiveMinimum;
153 0 0       0 return 1 if $instance != $minimum;
154              
155 0         0 push @{$data->{errors}}, error(
  0         0  
156             message => "${instance} is equal to ${minimum}",
157             instance_path => $instance_path,
158             schema_path => $schema_path
159             );
160 0         0 return 0;
161             }
162              
163             sub minItems {
164 16     16 0 68 my ($self, $instance, $min, $schema, $instance_path, $schema_path, $data) = @_;
165 16 50       45 return 1 unless $self->check_type($instance, 'array');
166 16 100       71 return 1 if scalar(@$instance) >= $min;
167 1         3 push @{$data->{errors}}, error(
  1         11  
168             message => "minItems (>= ${min}) constraint violated",
169             instance_path => $instance_path,
170             schema_path => $schema_path
171             );
172 1         4 return 0;
173             }
174              
175             sub maxItems {
176 4     4 0 14 my ($self, $instance, $max, $schema, $instance_path, $schema_path, $data) = @_;
177 4 100       13 return 1 unless $self->check_type($instance, 'array');
178 3 100       15 return 1 if scalar(@$instance) <= $max;
179 1         3 push @{$data->{errors}}, error(
  1         10  
180             message => "maxItems (<= ${max}) constraint violated",
181             instance_path => $instance_path,
182             schema_path => $schema_path
183             );
184 1         4 return 0;
185             }
186              
187             sub minLength {
188 3     3 0 11 my ($self, $instance, $min, $schema, $instance_path, $schema_path, $data) = @_;
189 3 50       10 return 1 unless $self->check_type($instance, 'string');
190 3 100       14 return 1 if length $instance >= $min;
191 1         3 push @{$data->{errors}}, error(
  1         9  
192             message => "minLength (>= ${min}) constraint violated",
193             instance_path => $instance_path,
194             schema_path => $schema_path
195             );
196 1         4 return 0;
197             }
198              
199             sub maxLength {
200 4     4 0 14 my ($self, $instance, $max, $schema, $instance_path, $schema_path, $data) = @_;
201 4 100       13 return 1 unless $self->check_type($instance, 'string');
202 3 100       16 return 1 if length $instance <= $max;
203 1         3 push @{$data->{errors}}, error(
  1         25  
204             message => "maxLength (<= ${max}) constraint violated",
205             instance_path => $instance_path,
206             schema_path => $schema_path
207             );
208 1         5 return 0;
209             }
210              
211             sub dependencies {
212 37     37 0 103 my ($self, $instance, $dependencies, $schema, $instance_path, $schema_path, $data) = @_;
213              
214             # ignore non-object
215 37 100       100 return 1 unless $self->check_type($instance, 'object');
216              
217 34         91 my $result = 1;
218              
219 34         94 for my $prop (keys %$dependencies) {
220 64 100       155 next unless exists $instance->{$prop};
221 2         6 my $dep = $dependencies->{$prop};
222 2         6 my $spath = json_pointer->append($schema_path, $prop);
223              
224             # need strict check beacase of schema check
225 2 50       8 if ($self->check_type($dep, 'array', 1)) {
226 2         5 for my $idx (0 .. $#{$dep}) {
  2         9  
227 2         6 my $p = $dep->[$idx];
228 2 100       10 next if exists $instance->{$p};
229              
230 1         2 push @{$data->{errors}}, error(
  1         8  
231             message => "dependencies constraint violated: property $p is ommited",
232             instance_path => $instance_path,
233             schema_path => json_pointer->append($spath, $idx)
234             );
235 1         5 $result = 0;
236             }
237             } else {
238             # $dep is object or boolean (starting draft 6 boolean is valid schema)
239 0         0 my $r = $self->validator->_validate_schema($instance, $dep, $instance_path, $spath, $data);
240 0 0       0 $result = 0 unless $r;
241             }
242             }
243              
244 34         91 return $result;
245             }
246              
247             sub additionalItems {
248 10     10 0 34 my ($self, $instance, $additionalItems, $schema, $instance_path, $schema_path, $data) = @_;
249 10 50       47 return 1 unless $self->check_type($instance, 'array');
250             # need strict check beacase of schema check
251 10 50 50     45 return 1 if $self->check_type($schema->{items} // {}, 'object', 1);
252              
253 10         20 my $len_items = scalar @{$schema->{items}};
  10         33  
254              
255             # need strict check beacase of schema check
256 10 100       27 if ($self->check_type($additionalItems, 'boolean', 1)) {
257 8 50       62 return 1 if $additionalItems;
258 8 100       91 if (scalar @$instance > $len_items) {
259 2         4 push @{$data->{errors}}, error(
  2         13  
260             message => 'additionalItems constraint violated',
261             instance_path => $instance_path,
262             schema_path => $schema_path
263             );
264 2         9 return 0;
265             }
266              
267 6         30 return 1;
268             }
269              
270             # additionalItems is object
271              
272 2         6 my $result = 1;
273 2         6 my @items_last_part = @$instance[$len_items .. $#{$instance}];
  2         11  
274              
275 2         7 for my $index (0 .. $#items_last_part) {
276 6         13 my $item = $items_last_part[$index];
277              
278 6         16 my $ipath = json_pointer->append($instance_path, $len_items + $index);
279 6         17 my $r = $self->validator->_validate_schema($item, $additionalItems, $ipath, $schema_path, $data);
280 6 100       15 $result = 0 unless $r;
281             }
282              
283 2         7 return $result;
284             }
285              
286             sub additionalProperties {
287 143     143 0 395 my ($self, $instance, $addProps, $schema, $instance_path, $schema_path, $data) = @_;
288              
289 143         1585 warn 'additionalProperties';
290 143         7640 warn Dumper $instance;
291 143         17879 warn Dumper $addProps;
292              
293 143 100       12608 return 1 unless $self->check_type($instance, 'object');
294              
295 137   100     362 my $patterns = join '|', keys %{$schema->{patternProperties} // {}};
  137         754  
296              
297 137         320 my @extra_props;
298 137         422 for my $p (keys %$instance) {
299 235 100 100     817 next if $schema->{properties} && exists $schema->{properties}{$p};
300 99 100 100     741 next if $patterns && $p =~ m/$patterns/u;
301 87         202 push @extra_props, $p;
302             }
303              
304 137         1208 warn 'extra_props';
305 137         4139 warn Dumper \@extra_props;
306              
307 137 100       11054 return 1 unless @extra_props;
308              
309             # need strict check beacase of schema check
310 57 100       245 if ($self->check_type($addProps, 'object', 1)) {
311 39         351 warn 'addProps is object';
312 39         820 my $result = 1;
313 39         97 for my $p (@extra_props) {
314 69         226 my $ipath = json_pointer->append($instance_path, $p);
315 69         223 my $r = $self->validator->_validate_schema($instance->{$p}, $addProps, $ipath, $schema_path, $data);
316 69         815 warn "check $p: $r";
317 69         3668 warn Dumper $data->{errors};
318 69 100       5254 $result = 0 unless $r;
319             }
320 39         203 return $result;
321             }
322              
323             # addProps is boolean
324              
325 18 50       132 return 1 if $addProps;
326              
327 18         198 push @{$data->{errors}}, error(
  18         161  
328             message => 'additionalProperties constraint violated; properties: ' . join(', ', @extra_props),
329             instance_path => $instance_path,
330             schema_path => $schema_path
331             );
332 18         71 return 0;
333             }
334              
335             sub allOf {
336 69     69 0 298 my ($self, $instance, $allOf, $schema, $instance_path, $schema_path, $data) = @_;
337              
338 69         158 my $result = 1;
339 69         179 for my $idx (0 .. $#{$allOf}) {
  69         245  
340 160         321 my $subschema = $allOf->[$idx];
341 160         399 my $spath = json_pointer->append($schema_path, $idx);
342 160         426 my $r = $self->validator->_validate_schema($instance, $subschema, $instance_path, $spath, $data);
343 160 100       469 $result = 0 unless $r;
344             }
345              
346 69         180 return $result;
347             }
348              
349             sub anyOf {
350 68     68 0 191 my ($self, $instance, $anyOf, $schema, $instance_path, $schema_path, $data) = @_;
351              
352 68         129 my $errors = $data->{errors};
353 68         127 my $local_errors = [];
354              
355 68         117 my $result = 0;
356 68         202 for my $idx (0 .. $#$anyOf) {
357 70         161 $data->{errors} = [];
358 70         163 my $spath = json_pointer->append($schema_path, $idx);
359 70         180 $result = $self->validator->_validate_schema($instance, $anyOf->[$idx], $instance_path, $spath, $data);
360 70 100       156 unless ($result) {
361 3         17 push @{$local_errors}, error(
362             message => qq'${idx} part of "anyOf" has errors',
363             context => $data->{errors},
364 3         4 instance_path => $instance_path,
365             schema_path => $spath
366             );
367             }
368 70 100       171 last if $result;
369             }
370 68         165 $data->{errors} = $errors;
371 68 100       227 return 1 if $result;
372              
373 1         4 push @{$data->{errors}}, error(
  1         5  
374             message => 'instance does not satisfy any schema of "anyOf"',
375             context => $local_errors,
376             instance_path => $instance_path,
377             schema_path => $schema_path
378             );
379 1         4 return 0;
380             }
381              
382             sub oneOf {
383 64     64 0 177 my ($self, $instance, $oneOf, $schema, $instance_path, $schema_path, $data) = @_;
384              
385 64         118 my $errors = $data->{errors};
386 64         172 my ($local_errors, $valid_schemas) = ([], []);
387              
388 64         100 my $num = 0;
389 64         200 for my $idx (0 .. $#$oneOf) {
390 136         280 $data->{errors} = [];
391 136         320 my $spath = json_pointer->append($schema_path, $idx);
392 136         307 my $r = $self->validator->_validate_schema($instance, $oneOf->[$idx], $instance_path, $spath, $data);
393 136 100       310 if ($r) {
394 64         92 push @{$valid_schemas}, $spath;
  64         154  
395             } else {
396 72         294 push @{$local_errors}, error(
397             message => qq'${idx} part of "oneOf" has errors',
398             context => $data->{errors},
399 72         115 instance_path => $instance_path,
400             schema_path => $spath
401             );
402             }
403 136 100       380 ++$num if $r;
404             }
405 64         133 $data->{errors} = $errors;
406 64 100       351 return 1 if $num == 1;
407              
408 2 100       9 if ($num > 1) {
409 1         3 push @{$data->{errors}}, error(
  1         8  
410             message => 'instance is valid under more than one schema of "oneOf": ' . join(' ', @$valid_schemas),
411             instance_path => $instance_path,
412             schema_path => $schema_path
413             );
414             } else {
415 1         3 push @{$data->{errors}}, error(
  1         5  
416             message => 'instance is not valid under any of given schemas of "oneOf"',
417             context => $local_errors,
418             instance_path => $instance_path,
419             schema_path => $schema_path
420             );
421             }
422              
423 2         7 return 0;
424             }
425              
426             sub enum {
427 181     181 0 467 my ($self, $instance, $enum, $schema, $instance_path, $schema_path, $data) = @_;
428              
429 181         345 my $result = 0;
430 181         384 for my $e (@$enum) {
431             # schema must have strict check
432 601 100 33     1278 if ($self->check_type($e, 'boolean', 1)) {
    50 33        
    100 0        
    50          
    0          
433 4 50       10 $result = $self->check_type($instance, 'boolean')
434             ? unbool($instance) eq unbool($e)
435             : 0
436             } elsif ($self->check_type($e, 'object', 1) || $self->check_type($e, 'array', 1)) {
437 0 0 0     0 $result = $self->check_type($instance, 'object') ||
438             $self->check_type($instance, 'array')
439             ? serialize($instance) eq serialize($e)
440             : 0;
441             } elsif ($self->check_type($e, 'number', 1)) {
442 4 50       10 $result = $self->check_type($instance, 'number')
443             ? $e == $instance
444             : 0;
445             } elsif (defined $e && defined $instance) {
446 593         1134 $result = $e eq $instance;
447             } elsif (!defined $e && !defined $instance) {
448 0         0 $result = 1;
449             } else {
450 0         0 $result = 0;
451             }
452 601 100       1488 last if $result;
453             }
454              
455 181 100       579 return 1 if $result;
456              
457 19         35 push @{$data->{errors}}, error(
  19         74  
458             message => "instance is not of enums",
459             instance_path => $instance_path,
460             schema_path => $schema_path
461             );
462 19         61 return 0;
463             }
464              
465             sub items {
466 49     49 0 154 my ($self, $instance, $items, $schema, $instance_path, $schema_path, $data) = @_;
467 49 100       138 return 1 unless $self->check_type($instance, 'array');
468              
469 47         109 my $result = 1;
470 47 100       121 if ($self->check_type($items, 'array', 1)) {
471 10 100       42 my $min = $#{$items} > $#{$instance} ? $#{$instance} : $#{$items};
  10         29  
  10         50  
  2         7  
  8         26  
472 10         40 for my $i (0 .. $min) {
473 17         39 my $item = $instance->[$i];
474 17         34 my $subschema = $items->[$i];
475 17         52 my $spath = json_pointer->append($schema_path, $i);
476 17         43 my $ipath = json_pointer->append($instance_path, $i);
477 17         47 my $r = $self->validator->_validate_schema($item, $subschema, $ipath, $spath, $data);
478 17 50       118 $result = 0 unless $r;
479             }
480             } else {
481             # items is object
482 37         73 for my $i (0 .. $#{$instance}) {
  37         136  
483 71         154 my $item = $instance->[$i];
484 71         168 my $ipath = json_pointer->append($instance_path, $i);
485 71         183 my $r = $self->validator->_validate_schema($item, $items, $ipath, $schema_path, $data);
486 71 100       270 $result = 0 unless $r;
487             }
488             }
489 47         143 return $result;
490             }
491              
492             sub format {
493 53     53 0 159 my ($self, $instance, $format, $schema, $instance_path, $schema_path, $data) = @_;
494 53 50       188 return 1 unless exists FORMAT_VALIDATIONS->{$format};
495              
496 53         91 my ($type, $checker) = @{FORMAT_VALIDATIONS->{$format}};
  53         151  
497 53 100       164 return 1 unless $self->check_type($instance, $type);
498              
499 48         187 my $result = $checker->($instance);
500 48 50       233 return 1 if $result;
501              
502 0         0 push @{$data->{errors}}, error(
  0         0  
503             message => "instance is not $format",
504             instance_path => $instance_path,
505             schema_path => $schema_path
506             );
507 0         0 return 0;
508             }
509              
510             sub maxProperties {
511 2     2 0 9 my ($self, $instance, $maxProperties, $schema, $instance_path, $schema_path, $data) = @_;
512 2 50       8 return 1 unless $self->check_type($instance, 'object');
513 2 100       11 return 1 if scalar(keys %$instance) <= $maxProperties;
514              
515 1         11 push @{$data->{errors}}, error(
  1         12  
516             message => "instance has more than $maxProperties properties",
517             instance_path => $instance_path,
518             schema_path => $schema_path
519             );
520 1         4 return 0;
521             }
522              
523             sub minProperties {
524 7     7 0 25 my ($self, $instance, $minProperties, $schema, $instance_path, $schema_path, $data) = @_;
525 7 50       26 return 1 unless $self->check_type($instance, 'object');
526 7 100       40 return 1 if scalar(keys %$instance) >= $minProperties;
527              
528 1         5 push @{$data->{errors}}, error(
  1         9  
529             message => "instance has less than $minProperties properties",
530             instance_path => $instance_path,
531             schema_path => $schema_path
532             );
533 1         4 return 0;
534             }
535              
536             sub multipleOf {
537 5     5 0 20 my ($self, $instance, $multipleOf, $schema, $instance_path, $schema_path, $data) = @_;
538 5 50       17 return 1 unless $self->check_type($instance, 'number');
539              
540 5         15 my $result = 1;
541              
542 5         13 my $div = $instance / $multipleOf;
543 5 100 66     34 $result = 0 if $div == 'Inf' || int($div) != $div;
544              
545 5 100       18 return 1 if $result;
546              
547 2         5 push @{$data->{errors}}, error(
  2         27  
548             message => "instance is not multiple of $multipleOf",
549             instance_path => $instance_path,
550             schema_path => $schema_path
551             );
552 2         7 return 0;
553             }
554              
555             sub not {
556 62     62 0 148 my ($self, $instance, $not, $schema, $instance_path, $schema_path, $data) = @_;
557              
558 62         118 my $errors = $data->{errors};
559 62         124 $data->{errors} = [];
560              
561             # not is schema
562 62         141 my $result = $self->validator->_validate_schema($instance, $not, $instance_path, $schema_path, $data);
563 62         192 $data->{errors} = $errors;
564 62 100       184 return 1 unless $result;
565              
566 1         3 push @{$data->{errors}}, error(
  1         7  
567             message => 'instance satisfies the schema defined in \"not\" keyword',
568             instance_path => $instance_path,
569             schema_path => $schema_path
570             );
571 1         5 return 0;
572             }
573              
574             sub pattern {
575 15     15 0 53 my ($self, $instance, $pattern, $schema, $instance_path, $schema_path, $data) = @_;
576 15 50       50 return 1 unless $self->check_type($instance, 'string');
577 15 100       275 return 1 if $instance =~ m/$pattern/u;
578              
579 4         14 push @{$data->{errors}}, error(
  4         42  
580             message => "instance does not match $pattern",
581             instance_path => $instance_path,
582             schema_path => $schema_path
583             );
584 4         18 return 0;
585             }
586              
587             sub patternProperties {
588 135     135 0 351 my ($self, $instance, $patternProperties, $schema, $instance_path, $schema_path, $data) = @_;
589 135 50       314 return 1 unless $self->check_type($instance, 'object');
590              
591 135         281 my $result = 1;
592 135         343 for my $pattern (keys %$patternProperties) {
593 147         287 my $subschema = $patternProperties->{$pattern};
594 147         333 my $spath = json_pointer->append($schema_path, $pattern);
595 147         389 for my $k (keys %$instance) {
596 260         501 my $v = $instance->{$k};
597 260 100       2421 if ($k =~ m/$pattern/u) {
598 35         128 my $ipath = json_pointer->append($instance_path, $k);
599 35         251 my $r = $self->validator->_validate_schema($v, $subschema, $ipath, $spath, $data);
600 35 100       124 $result = 0 unless $r;
601             }
602             }
603             }
604 135         472 return $result;
605             }
606              
607             sub properties {
608 413     413 0 1136 my ($self, $instance, $properties, $schema, $instance_path, $schema_path, $data) = @_;
609 413 100       1083 return 1 unless $self->check_type($instance, 'object');
610              
611 412         879 my $result = 1;
612 412         1844 for my $prop (keys %$properties) {
613 5323 100       10543 next unless exists $instance->{$prop};
614              
615 693         1267 my $subschema = $properties->{$prop};
616 693         1693 my $spath = json_pointer->append($schema_path, $prop);
617 693         1645 my $ipath = json_pointer->append($instance_path, $prop);
618 693         1859 my $r = $self->validator->_validate_schema($instance->{$prop}, $subschema, $ipath, $spath, $data);
619 693 100       1993 $result = 0 unless $r;
620             }
621 412         1248 return $result;
622             }
623              
624             sub required {
625 181     181 0 474 my ($self, $instance, $required, $schema, $instance_path, $schema_path, $data) = @_;
626 181 50       420 return 1 unless $self->check_type($instance, 'object');
627              
628 181         352 my $result = 1;
629 181         286 for my $idx (0 .. $#{$required}) {
  181         500  
630 240         501 my $prop = $required->[$idx];
631 240 100       609 next if exists $instance->{$prop};
632 131         209 push @{$data->{errors}}, error(
  131         472  
633             message => qq{instance does not have required property "$prop"},
634             instance_path => $instance_path,
635             schema_path => json_pointer->append($schema_path, $idx)
636             );
637 131         349 $result = 0;
638             }
639 181         477 return $result;
640             }
641              
642             # doesn't work for string that looks like number with the same number in array
643             sub uniqueItems {
644 24     24 0 89 my ($self, $instance, $uniqueItems, $schema, $instance_path, $schema_path, $data) = @_;
645 24 50       67 return 1 unless $self->check_type($instance, 'array');
646             # uniqueItems is boolean
647 24 50       167 return 1 unless $uniqueItems;
648              
649             my %hash = map {
650 24         250 my $type = detect_type($_, $self->strict);
  45         118  
651              
652 45         83 my $value;
653 45 100 66     215 if ($type eq 'null') {
    100          
    100          
654 1         3 $value = ''
655             } elsif ($type eq 'object' || $type eq 'array') {
656 4         16 $value = serialize($_);
657             } elsif ($type eq 'boolean') {
658 10         27 $value = "$_";
659             } else {
660             # integer/number/string
661 30         53 $value = $_;
662             }
663              
664 45         149 my $key = "${type}#${value}";
665 45         187 $key => 1;
666             } @$instance;
667 24 100       165 return 1 if scalar(keys %hash) == scalar @$instance;
668 2         7 push @{$data->{errors}}, error(
  2         12  
669             message => "instance has non-unique elements",
670             instance_path => $instance_path,
671             schema_path => $schema_path
672             );
673 2         10 return 0;
674             }
675              
676             sub ref {
677 388     388 0 1039 my ($self, $instance, $ref, $origin_schema, $instance_path, $schema_path, $data) = @_;
678              
679 388         825 my $scope = $self->validator->scope;
680 388         1392 $ref = URI->new($ref);
681 388 100       18310 $ref = $ref->abs($scope) if $scope;
682              
683 388         83725 my ($current_scope, $schema) = $self->validator->resolver->resolve($ref);
684              
685 388 50       2061 croak "schema not resolved by ref $ref" unless $schema;
686              
687 388         635 push @{$self->validator->scopes}, $current_scope;
  388         835  
688              
689 388         737 my $result = eval {
690 388         749 $self->validator->_validate_schema($instance, $schema, $instance_path, $schema_path, $data, apply_scope => 0);
691             };
692              
693 388 50       920 if ($@) {
694 0         0 $result = 0;
695 0         0 push @{$data->{errors}}, error(
  0         0  
696             message => "exception: $@",
697             instance_path => $instance_path,
698             schema_path => $schema_path
699             );
700             }
701              
702 388         621 pop @{$self->validator->scopes};
  388         888  
703              
704 388         1225 return $result;
705             }
706              
707             1;
708              
709             __END__