json-schema 2.4.1 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +6 -14
- data/README.textile +58 -7
- data/lib/json-schema.rb +3 -1
- data/lib/json-schema/attributes/additionalitems.rb +14 -11
- data/lib/json-schema/attributes/additionalproperties.rb +33 -43
- data/lib/json-schema/attributes/anyof.rb +4 -0
- data/lib/json-schema/attributes/dependencies.rb +31 -19
- data/lib/json-schema/attributes/disallow.rb +2 -3
- data/lib/json-schema/attributes/divisibleby.rb +11 -7
- data/lib/json-schema/attributes/enum.rb +14 -16
- data/lib/json-schema/attributes/format.rb +4 -7
- data/lib/json-schema/attributes/formats/date_time_v4.rb +5 -8
- data/lib/json-schema/attributes/formats/ip.rb +41 -0
- data/lib/json-schema/attributes/formats/uri.rb +10 -8
- data/lib/json-schema/attributes/items.rb +15 -16
- data/lib/json-schema/attributes/limit.rb +179 -0
- data/lib/json-schema/attributes/maxdecimal.rb +7 -6
- data/lib/json-schema/attributes/multipleof.rb +4 -11
- data/lib/json-schema/attributes/not.rb +1 -1
- data/lib/json-schema/attributes/oneof.rb +15 -6
- data/lib/json-schema/attributes/pattern.rb +7 -6
- data/lib/json-schema/attributes/patternproperties.rb +9 -12
- data/lib/json-schema/attributes/properties.rb +55 -39
- data/lib/json-schema/attributes/properties_optional.rb +13 -12
- data/lib/json-schema/attributes/ref.rb +4 -4
- data/lib/json-schema/attributes/required.rb +16 -13
- data/lib/json-schema/attributes/type.rb +13 -18
- data/lib/json-schema/attributes/type_v4.rb +11 -18
- data/lib/json-schema/attributes/uniqueitems.rb +5 -7
- data/lib/json-schema/schema.rb +8 -8
- data/lib/json-schema/schema/#validator.rb# +37 -0
- data/lib/json-schema/schema/reader.rb +113 -0
- data/lib/json-schema/util/uri.rb +16 -0
- data/lib/json-schema/validator.rb +123 -128
- data/lib/json-schema/validators/draft1.rb +1 -1
- data/lib/json-schema/validators/draft2.rb +1 -1
- data/lib/json-schema/validators/draft3.rb +1 -1
- data/lib/json-schema/validators/draft4.rb +1 -1
- data/lib/json-schema/validators/hyper-draft4.rb +1 -1
- data/test/schemas/address_microformat.json +18 -0
- data/test/schemas/definition_schema.json +15 -0
- data/test/schemas/ref john with spaces schema.json +11 -0
- data/test/schemas/relative_definition_schema.json +8 -0
- data/test/test_all_of_ref_schema.rb +12 -15
- data/test/test_any_of_ref_schema.rb +7 -9
- data/test/test_bad_schema_ref.rb +18 -12
- data/test/test_common_test_suite.rb +45 -29
- data/test/test_custom_format.rb +2 -3
- data/test/test_definition.rb +15 -0
- data/test/test_extended_schema.rb +25 -31
- data/test/test_extends_and_additionalProperties.rb +23 -21
- data/test/test_files_v3.rb +14 -23
- data/test/test_fragment_resolution.rb +6 -7
- data/test/test_fragment_validation_with_ref.rb +2 -8
- data/test/test_full_validation.rb +2 -3
- data/test/test_helper.rb +46 -1
- data/test/test_initialize_data.rb +118 -0
- data/test/test_jsonschema_draft1.rb +48 -600
- data/test/test_jsonschema_draft2.rb +48 -699
- data/test/test_jsonschema_draft3.rb +91 -861
- data/test/test_jsonschema_draft4.rb +173 -812
- data/test/test_list_option.rb +6 -7
- data/test/{test_merge_misisng_values.rb → test_merge_missing_values.rb} +2 -3
- data/test/test_minitems.rb +2 -4
- data/test/test_one_of.rb +9 -19
- data/test/test_ruby_schema.rb +5 -14
- data/test/test_schema_loader.rb +74 -0
- data/test/test_schema_type_attribute.rb +2 -3
- data/test/test_schema_validation.rb +4 -5
- data/test/test_stringify.rb +2 -3
- data/test/test_uri_related.rb +67 -0
- data/test/test_validator.rb +53 -0
- metadata +129 -51
- data/lib/json-schema/attributes/dependencies_v4.rb +0 -27
- data/lib/json-schema/attributes/formats/ip4.rb +0 -20
- data/lib/json-schema/attributes/formats/ip6.rb +0 -20
- data/lib/json-schema/attributes/maximum.rb +0 -17
- data/lib/json-schema/attributes/maximum_inclusive.rb +0 -17
- data/lib/json-schema/attributes/maxitems.rb +0 -14
- data/lib/json-schema/attributes/maxlength.rb +0 -16
- data/lib/json-schema/attributes/maxproperties.rb +0 -14
- data/lib/json-schema/attributes/minimum.rb +0 -17
- data/lib/json-schema/attributes/minimum_inclusive.rb +0 -17
- data/lib/json-schema/attributes/minitems.rb +0 -14
- data/lib/json-schema/attributes/minlength.rb +0 -16
- data/lib/json-schema/attributes/minproperties.rb +0 -14
- data/lib/json-schema/attributes/properties_v4.rb +0 -58
- data/lib/json-schema/uri/file.rb +0 -36
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'json-schema/attributes/format'
|
2
|
+
require 'ipaddr'
|
3
|
+
require 'socket'
|
4
|
+
|
5
|
+
module JSON
|
6
|
+
class Schema
|
7
|
+
class IPFormat < FormatAttribute
|
8
|
+
def self.validate(current_schema, data, fragments, processor, validator, options = {})
|
9
|
+
return unless data.is_a?(String)
|
10
|
+
|
11
|
+
begin
|
12
|
+
ip = IPAddr.new(data)
|
13
|
+
rescue ArgumentError => e
|
14
|
+
raise e unless e.message == 'invalid address'
|
15
|
+
end
|
16
|
+
|
17
|
+
family = ip_version == 6 ? Socket::AF_INET6 : Socket::AF_INET
|
18
|
+
unless ip && ip.family == family
|
19
|
+
error_message = "The property '#{build_fragment(fragments)}' must be a valid IPv#{ip_version} address"
|
20
|
+
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.ip_version
|
25
|
+
raise NotImplementedError
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class IP4Format < IPFormat
|
30
|
+
def self.ip_version
|
31
|
+
4
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class IP6Format < IPFormat
|
36
|
+
def self.ip_version
|
37
|
+
6
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -1,16 +1,18 @@
|
|
1
1
|
require 'json-schema/attribute'
|
2
|
-
require 'uri'
|
2
|
+
require 'addressable/uri'
|
3
3
|
module JSON
|
4
4
|
class Schema
|
5
5
|
class UriFormat < FormatAttribute
|
6
6
|
def self.validate(current_schema, data, fragments, processor, validator, options = {})
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
7
|
+
return unless data.is_a?(String)
|
8
|
+
error_message = "The property '#{build_fragment(fragments)}' must be a valid URI"
|
9
|
+
begin
|
10
|
+
# TODO
|
11
|
+
# Addressable only throws an exception on to_s for invalid URI strings, although it
|
12
|
+
# probably should throughout parse already - https://github.com/sporkmonger/addressable/issues/177
|
13
|
+
Addressable::URI.parse(data).to_s
|
14
|
+
rescue Addressable::URI::InvalidURIError
|
15
|
+
validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
|
14
16
|
end
|
15
17
|
end
|
16
18
|
end
|
@@ -4,22 +4,21 @@ module JSON
|
|
4
4
|
class Schema
|
5
5
|
class ItemsAttribute < Attribute
|
6
6
|
def self.validate(current_schema, data, fragments, processor, validator, options = {})
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
end
|
7
|
+
return unless data.is_a?(Array)
|
8
|
+
|
9
|
+
items = current_schema.schema['items']
|
10
|
+
case items
|
11
|
+
when Hash
|
12
|
+
schema = JSON::Schema.new(items, current_schema.uri, validator)
|
13
|
+
data.each_with_index do |item, i|
|
14
|
+
schema.validate(item, fragments + [i.to_s], processor, options)
|
15
|
+
end
|
16
|
+
|
17
|
+
when Array
|
18
|
+
items.each_with_index do |item_schema, i|
|
19
|
+
schema = JSON::Schema.new(item_schema, current_schema.uri, validator)
|
20
|
+
schema.validate(data[i], fragments + [i.to_s], processor, options)
|
21
|
+
end
|
23
22
|
end
|
24
23
|
end
|
25
24
|
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
require 'json-schema/attribute'
|
2
|
+
|
3
|
+
module JSON
|
4
|
+
class Schema
|
5
|
+
class LimitAttribute < Attribute
|
6
|
+
def self.validate(current_schema, data, fragments, processor, validator, options = {})
|
7
|
+
schema = current_schema.schema
|
8
|
+
return unless data.is_a?(acceptable_type) && invalid?(schema, value(data))
|
9
|
+
|
10
|
+
property = build_fragment(fragments)
|
11
|
+
description = error_message(schema)
|
12
|
+
message = format("The property '%s' %s", property, description)
|
13
|
+
validation_error(processor, message, fragments, current_schema, self, options[:record_errors])
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.invalid?(schema, data)
|
17
|
+
exclusive = exclusive?(schema)
|
18
|
+
limit = limit(schema)
|
19
|
+
|
20
|
+
if limit_name.start_with?('max')
|
21
|
+
exclusive ? data >= limit : data > limit
|
22
|
+
else
|
23
|
+
exclusive ? data <= limit : data < limit
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.limit(schema)
|
28
|
+
schema[limit_name]
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.exclusive?(schema)
|
32
|
+
false
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.value(data)
|
36
|
+
data
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.acceptable_type
|
40
|
+
raise NotImplementedError
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.error_message(schema)
|
44
|
+
raise NotImplementedError
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.limit_name
|
48
|
+
raise NotImplementedError
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class MinLengthAttribute < LimitAttribute
|
53
|
+
def self.acceptable_type
|
54
|
+
String
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.limit_name
|
58
|
+
'minLength'
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.error_message(schema)
|
62
|
+
"was not of a minimum string length of #{limit(schema)}"
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.value(data)
|
66
|
+
data.length
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class MaxLengthAttribute < MinLengthAttribute
|
71
|
+
def self.limit_name
|
72
|
+
'maxLength'
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.error_message(schema)
|
76
|
+
"was not of a maximum string length of #{limit(schema)}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
class MinItemsAttribute < LimitAttribute
|
81
|
+
def self.acceptable_type
|
82
|
+
Array
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.value(data)
|
86
|
+
data.length
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.limit_name
|
90
|
+
'minItems'
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.error_message(schema)
|
94
|
+
"did not contain a minimum number of items #{limit(schema)}"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class MaxItemsAttribute < MinItemsAttribute
|
99
|
+
def self.limit_name
|
100
|
+
'maxItems'
|
101
|
+
end
|
102
|
+
|
103
|
+
def self.error_message(schema)
|
104
|
+
"had more items than the allowed #{limit(schema)}"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
class MinPropertiesAttribute < LimitAttribute
|
109
|
+
def self.acceptable_type
|
110
|
+
Hash
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.value(data)
|
114
|
+
data.size
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.limit_name
|
118
|
+
'minProperties'
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.error_message(schema)
|
122
|
+
"did not contain a minimum number of properties #{limit(schema)}"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
class MaxPropertiesAttribute < MinPropertiesAttribute
|
127
|
+
def self.limit_name
|
128
|
+
'maxProperties'
|
129
|
+
end
|
130
|
+
|
131
|
+
def self.error_message(schema)
|
132
|
+
"had more properties than the allowed #{limit(schema)}"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
class NumericLimitAttribute < LimitAttribute
|
137
|
+
def self.acceptable_type
|
138
|
+
Numeric
|
139
|
+
end
|
140
|
+
|
141
|
+
def self.error_message(schema)
|
142
|
+
exclusivity = exclusive?(schema) ? 'exclusively' : 'inclusively'
|
143
|
+
format("did not have a %s value of %s, %s", limit_name, limit(schema), exclusivity)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
class MaximumAttribute < NumericLimitAttribute
|
148
|
+
def self.limit_name
|
149
|
+
'maximum'
|
150
|
+
end
|
151
|
+
|
152
|
+
def self.exclusive?(schema)
|
153
|
+
schema['exclusiveMaximum']
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
class MaximumInclusiveAttribute < MaximumAttribute
|
158
|
+
def self.exclusive?(schema)
|
159
|
+
schema['maximumCanEqual'] == false
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
class MinimumAttribute < NumericLimitAttribute
|
164
|
+
def self.limit_name
|
165
|
+
'minimum'
|
166
|
+
end
|
167
|
+
|
168
|
+
def self.exclusive?(schema)
|
169
|
+
schema['exclusiveMinimum']
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
class MinimumInclusiveAttribute < MinimumAttribute
|
174
|
+
def self.exclusive?(schema)
|
175
|
+
schema['minimumCanEqual'] == false
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
@@ -4,12 +4,13 @@ module JSON
|
|
4
4
|
class Schema
|
5
5
|
class MaxDecimalAttribute < Attribute
|
6
6
|
def self.validate(current_schema, data, fragments, processor, validator, options = {})
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
7
|
+
return unless data.is_a?(Numeric)
|
8
|
+
|
9
|
+
max_decimal_places = current_schema.schema['maxDecimal']
|
10
|
+
s = data.to_s.split(".")[1]
|
11
|
+
if s && s.length > max_decimal_places
|
12
|
+
message = "The property '#{build_fragment(fragments)}' had more decimal places than the allowed #{max_decimal_places}"
|
13
|
+
validation_error(processor, message, fragments, current_schema, self, options[:record_errors])
|
13
14
|
end
|
14
15
|
end
|
15
16
|
end
|
@@ -1,17 +1,10 @@
|
|
1
|
-
require 'json-schema/
|
1
|
+
require 'json-schema/attributes/divisibleby'
|
2
2
|
|
3
3
|
module JSON
|
4
4
|
class Schema
|
5
|
-
class MultipleOfAttribute <
|
6
|
-
def self.
|
7
|
-
|
8
|
-
if current_schema.schema['multipleOf'] == 0 ||
|
9
|
-
current_schema.schema['multipleOf'] == 0.0 ||
|
10
|
-
(BigDecimal.new(data.to_s) % BigDecimal.new(current_schema.schema['multipleOf'].to_s)).to_f != 0
|
11
|
-
message = "The property '#{build_fragment(fragments)}' was not divisible by #{current_schema.schema['multipleOf']}"
|
12
|
-
validation_error(processor, message, fragments, current_schema, self, options[:record_errors])
|
13
|
-
end
|
14
|
-
end
|
5
|
+
class MultipleOfAttribute < DivisibleByAttribute
|
6
|
+
def self.keyword
|
7
|
+
'multipleOf'
|
15
8
|
end
|
16
9
|
end
|
17
10
|
end
|
@@ -4,10 +4,10 @@ module JSON
|
|
4
4
|
class Schema
|
5
5
|
class NotAttribute < Attribute
|
6
6
|
def self.validate(current_schema, data, fragments, processor, validator, options = {})
|
7
|
-
|
8
7
|
schema = JSON::Schema.new(current_schema.schema['not'],current_schema.uri,validator)
|
9
8
|
failed = true
|
10
9
|
errors_copy = processor.validation_errors.clone
|
10
|
+
|
11
11
|
begin
|
12
12
|
schema.validate(data,fragments,processor,options)
|
13
13
|
# If we're recording errors, we don't throw an exception. Instead, check the errors array length
|
@@ -5,25 +5,34 @@ module JSON
|
|
5
5
|
class OneOfAttribute < Attribute
|
6
6
|
def self.validate(current_schema, data, fragments, processor, validator, options = {})
|
7
7
|
validation_errors = 0
|
8
|
-
current_schema.schema['oneOf']
|
8
|
+
one_of = current_schema.schema['oneOf']
|
9
|
+
|
10
|
+
original_data = data.is_a?(Hash) ? data.clone : data
|
11
|
+
success_data = nil
|
12
|
+
|
13
|
+
one_of.each do |element|
|
9
14
|
schema = JSON::Schema.new(element,current_schema.uri,validator)
|
10
15
|
|
11
16
|
begin
|
12
17
|
# need to raise exceptions on error because
|
13
18
|
# schema.validate doesn't reliably return true/false
|
14
19
|
schema.validate(data,fragments,processor,options.merge(:record_errors => false))
|
20
|
+
success_data = data.is_a?(Hash) ? data.clone : data
|
15
21
|
rescue ValidationError
|
16
22
|
validation_errors += 1
|
17
23
|
end
|
18
24
|
|
25
|
+
data = original_data
|
26
|
+
end
|
27
|
+
|
28
|
+
if validation_errors == one_of.length - 1
|
29
|
+
data = success_data
|
30
|
+
return
|
19
31
|
end
|
20
32
|
|
21
|
-
|
22
|
-
when current_schema.schema['oneOf'].length - 1 # correct, matched only one
|
23
|
-
message = nil
|
24
|
-
when current_schema.schema['oneOf'].length # didn't match any
|
33
|
+
if validation_errors == one_of.length
|
25
34
|
message = "The property '#{build_fragment(fragments)}' of type #{data.class} did not match any of the required schemas"
|
26
|
-
else
|
35
|
+
else
|
27
36
|
message = "The property '#{build_fragment(fragments)}' of type #{data.class} matched more than one of the required schemas"
|
28
37
|
end
|
29
38
|
|
@@ -4,12 +4,13 @@ module JSON
|
|
4
4
|
class Schema
|
5
5
|
class PatternAttribute < Attribute
|
6
6
|
def self.validate(current_schema, data, fragments, processor, validator, options = {})
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
7
|
+
return unless data.is_a?(String)
|
8
|
+
|
9
|
+
pattern = current_schema.schema['pattern']
|
10
|
+
regexp = Regexp.new(pattern)
|
11
|
+
unless regexp.match(data)
|
12
|
+
message = "The property '#{build_fragment(fragments)}' value #{data.inspect} did not match the regex '#{pattern}'"
|
13
|
+
validation_error(processor, message, fragments, current_schema, self, options[:record_errors])
|
13
14
|
end
|
14
15
|
end
|
15
16
|
end
|
@@ -4,19 +4,16 @@ module JSON
|
|
4
4
|
class Schema
|
5
5
|
class PatternPropertiesAttribute < Attribute
|
6
6
|
def self.validate(current_schema, data, fragments, processor, validator, options = {})
|
7
|
-
|
8
|
-
current_schema.schema['patternProperties'].each do |property,property_schema|
|
9
|
-
r = Regexp.new(property)
|
7
|
+
return unless data.is_a?(Hash)
|
10
8
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
end
|
9
|
+
current_schema.schema['patternProperties'].each do |property, property_schema|
|
10
|
+
regexp = Regexp.new(property)
|
11
|
+
|
12
|
+
# Check each key in the data hash to see if it matches the regex
|
13
|
+
data.each do |key, value|
|
14
|
+
next unless regexp.match(key)
|
15
|
+
schema = JSON::Schema.new(property_schema, current_schema.uri, validator)
|
16
|
+
schema.validate(data[key], fragments + [key], processor, options)
|
20
17
|
end
|
21
18
|
end
|
22
19
|
end
|
@@ -3,55 +3,71 @@ require 'json-schema/attribute'
|
|
3
3
|
module JSON
|
4
4
|
class Schema
|
5
5
|
class PropertiesAttribute < Attribute
|
6
|
+
def self.required?(schema, options)
|
7
|
+
schema.fetch('required') { options[:strict] }
|
8
|
+
end
|
9
|
+
|
6
10
|
def self.validate(current_schema, data, fragments, processor, validator, options = {})
|
7
|
-
|
8
|
-
current_schema.schema['properties'].each do |property,property_schema|
|
9
|
-
if !data.has_key?(property.to_s) &&
|
10
|
-
property_schema['default'] &&
|
11
|
-
!property_schema['readonly'] &&
|
12
|
-
options[:insert_defaults]
|
13
|
-
default = property_schema['default']
|
14
|
-
data[property.to_s] = (default.is_a?(Hash) ? default.clone : default)
|
15
|
-
end
|
11
|
+
return unless data.is_a?(Hash)
|
16
12
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
end
|
13
|
+
schema = current_schema.schema
|
14
|
+
schema['properties'].each do |property, property_schema|
|
15
|
+
property = property.to_s
|
21
16
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
17
|
+
if !data.key?(property) &&
|
18
|
+
options[:insert_defaults] &&
|
19
|
+
property_schema['default'] &&
|
20
|
+
!property_schema['readonly']
|
21
|
+
default = property_schema['default']
|
22
|
+
data[property] = default.is_a?(Hash) ? default.clone : default
|
23
|
+
end
|
24
|
+
|
25
|
+
if required?(property_schema, options) && !data.has_key?(property)
|
26
|
+
message = "The property '#{build_fragment(fragments)}' did not contain a required property of '#{property}'"
|
27
|
+
validation_error(processor, message, fragments, current_schema, self, options[:record_errors])
|
28
|
+
end
|
29
|
+
|
30
|
+
if data.has_key?(property)
|
31
|
+
expected_schema = JSON::Schema.new(property_schema, current_schema.uri, validator)
|
32
|
+
expected_schema.validate(data[property], fragments + [property], processor, options)
|
28
33
|
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# When strict is true, ensure no undefined properties exist in the data
|
37
|
+
return unless options[:strict] == true && !schema.key?('additionalProperties')
|
38
|
+
|
39
|
+
diff = data.select do |k, v|
|
40
|
+
k = k.to_s
|
29
41
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
if r.match(k)
|
38
|
-
match = true
|
39
|
-
break
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
!current_schema.schema['properties'].has_key?(k.to_s) && !match
|
44
|
-
else
|
45
|
-
!current_schema.schema['properties'].has_key?(k.to_s)
|
42
|
+
if schema.has_key?('patternProperties')
|
43
|
+
match = false
|
44
|
+
schema['patternProperties'].each do |property, property_schema|
|
45
|
+
regexp = Regexp.new(property)
|
46
|
+
if regexp.match(k)
|
47
|
+
match = true
|
48
|
+
break
|
46
49
|
end
|
47
50
|
end
|
48
51
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
end
|
52
|
+
!schema['properties'].has_key?(k) && !match
|
53
|
+
else
|
54
|
+
!schema['properties'].has_key?(k)
|
53
55
|
end
|
54
56
|
end
|
57
|
+
|
58
|
+
if diff.size > 0
|
59
|
+
properties = data.to_a.map { |(key, _)| key }.join(', ')
|
60
|
+
message = "The property '#{build_fragment(fragments)}' contained undefined properties: '#{properties}"
|
61
|
+
validation_error(processor, message, fragments, current_schema, self, options[:record_errors])
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class PropertiesV4Attribute < PropertiesAttribute
|
67
|
+
# draft4 relies on its own RequiredAttribute validation at a higher level, rather than
|
68
|
+
# as an attribute of individual properties.
|
69
|
+
def self.required?(schema, options)
|
70
|
+
options[:strict] == true
|
55
71
|
end
|
56
72
|
end
|
57
73
|
end
|