Subject: | Union data type validation |
union data type validation broken because it doesn't check for the ref
cases before it checks for the type match cases. Also doesn't handle
boolean checks against null values.
Patch for Helper.pm and reasonably complete test for union types attached.
Subject: | 05union.t.txt |
use Test::More;
use strict;
use warnings;
use JSON::Schema;
#primitive types are object, array, string, number, integer, boolean, null or any
my $schema1 = JSON::Schema->new(
{ type => 'object'
, additionalProperties => 0
, properties =>
{ test =>
{ type => [ 'boolean', 'integer' ]
, required => 1
}
}
} );
my $schema2 = JSON::Schema->new(
{ type => 'object'
, additionalProperties => 0
, properties =>
{ test =>
{ type =>
[ { type=>"object"
, additionalProperties => 0
, properties=>
{ dog => { type=>"string", required=>1 } }
}
, { type => "object"
, additionalProperties => 0
, properties =>
{ sound =>
{ type => 'string'
, enum => ["bark","meow","squeak"]
, required => 1
}
}
}
]
, required => 1
}
}
});
my $schema3 = JSON::Schema->new(
{ type => 'object'
, additionalProperties => 0
, properties =>
{ test =>
{ type => [ qw/object array string number integer boolean null/ ], required => 1 }
}
} );
my $result = $schema1->validate({ test => "strang" });
ok !$result->valid, 'boolean or integer against string'
or map { diag "reason: $_" } $result->errors;
$result = $schema1->validate({ test => 1 });
ok $result->valid, 'boolean or integer against integer'
or map { diag "reason: $_" } $result->errors;
$result = $schema1->validate({ test => [ 'array' ] });
ok not($result->valid), 'boolean or integer against array'
or map { diag "reason: $_" } $result->errors;
$result = $schema1->validate({ test => { object => 'yipe' } });
ok !$result->valid, 'boolean or integer against object'
or map { diag "reason: $_" } $result->errors;
$result = $schema1->validate({ test => 1.1 });
ok not($result->valid), 'boolean or integer against number'
or map { diag "reason: $_" } $result->errors;
$result = $schema1->validate({ test => !!1 });
ok $result->valid, 'boolean or integer against boolean'
or map { diag "reason: $_" } $result->errors;
$result = $schema1->validate({ test => undef });
ok !$result->valid, 'boolean or integer against null'
or map { diag "reason: $_" } $result->errors;
$result = $schema2->validate({ test => { dog => "woof" } });
ok $result->valid, 'object or object against object a'
or map { diag "reason: $_" } $result->errors;
$result = $schema2->validate({ test => { sound => "meow" } });
ok $result->valid, 'object or object against object b nested enum pass'
or map { diag "reason: $_" } $result->errors;
$result = $schema2->validate({ test => { sound => "oink" } });
ok not($result->valid), 'object or object against object b enum fail'
or map { diag "reason: $_" } $result->errors;
$result = $schema2->validate({ test => { audible => "meow" } });
ok !$result->valid, 'object or object against invalid object'
or map { diag "reason: $_" } $result->errors;
$result = $schema2->validate({ test => 2 });
ok !$result->valid, 'object or object against integer'
or map { diag "reason: $_" } $result->errors;
$result = $schema2->validate({ test => 2.2 });
ok !$result->valid, 'object or object against number'
or map { diag "reason: $_" } $result->errors;
$result = $schema2->validate({ test => !1 });
ok !$result->valid, 'object or object against boolean'
or map { diag "reason: $_" } $result->errors;
$result = $schema2->validate({ test => undef });
ok !$result->valid, 'object or object against null'
or map { diag "reason: $_" } $result->errors;
$result = $schema2->validate({ test => { dog => undef } });
ok not($result->valid), 'object or object against object a bad inner type'
or map { diag "reason: $_" } $result->errors;
$result = $schema3->validate({ test => { dog => undef } });
ok $result->valid, 'all types against object'
or map { diag "reason: $_" } $result->errors;
$result = $schema3->validate({ test => [ 'dog' ] });
ok $result->valid, 'all types against array'
or map { diag "reason: $_" } $result->errors;
$result = $schema3->validate({ test => 'dog' });
ok $result->valid, 'all types against string'
or map { diag "reason: $_" } $result->errors;
$result = $schema3->validate({ test => 1.1 });
ok $result->valid, 'all types against number'
or map { diag "reason: $_" } $result->errors;
$result = $schema3->validate({ test => 1 });
ok $result->valid, 'all types against integer'
or map { diag "reason: $_" } $result->errors;
$result = $schema3->validate({ test => 1 });
ok $result->valid, 'all types against boolean'
or map { diag "reason: $_" } $result->errors;
$result = $schema3->validate({ test => undef });
ok $result->valid, 'all types against null'
or map { diag "reason: $_" } $result->errors;
done_testing;
Subject: | json-schema-helper-patch.txt |
--- ./lib/JSON/Schema/Helper.pm 2012-05-03 17:59:26.000000000 -0400
+++ /usr/local/share/perl/5.10.0/JSON/Schema/Helper.pm 2012-10-09 17:10:25.000000000 -0400
@@ -118,17 +118,12 @@
# and ($type eq 'null' ? $self->jsIsNull($value) : $self->jsMatchType($type, $value))
# and !(ref $value eq 'ARRAY' and $type eq 'array')
# and !($type eq 'integer' and $value % 1 == 0))
- if (!$self->jsMatchType($type, $value))
- {
- $addError->($self->jsGuessType($value)." value found, but a $type is required");
- return @E;
- }
if (ref $type eq 'ARRAY')
{
my @unionErrors;
TYPE: foreach my $t (@$type)
{
- @unionErrors = @{ $self->checkType($t, $value, $path, $_changing, $schema) };
+ @unionErrors = $self->checkType($t, $value, $path, $_changing, $schema);
last unless @unionErrors;
}
return @unionErrors if @unionErrors;
@@ -138,6 +133,11 @@
local $self->{errors} = [];
$self->checkProp($value, $type, $path, undef, $_changing);
return @{ $self->{errors} };
+ }
+ elsif (!$self->jsMatchType($type, $value))
+ {
+ $addError->($self->jsGuessType($value)." value found, but a $type is required");
+ return @E;
}
}
return;
@@ -494,6 +494,7 @@
if (lc $type eq 'boolean')
{
+ return FALSE if ref $value eq 'JSON::Schema::Null';
return TRUE if (ref $value eq 'SCALAR' and $$value==0 || $$value==1);
return TRUE if ($value eq TRUE);
return TRUE if ($value eq FALSE);