json-schema 1.0.3 → 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/README.textile +1 -1
- data/lib/json-schema/attributes/additionalitems.rb +1 -1
- data/lib/json-schema/attributes/additionalproperties.rb +1 -1
- data/lib/json-schema/attributes/dependencies.rb +2 -2
- data/lib/json-schema/attributes/divisibleby.rb +1 -1
- data/lib/json-schema/attributes/enum.rb +1 -1
- data/lib/json-schema/attributes/format.rb +23 -23
- data/lib/json-schema/attributes/maxdecimal.rb +1 -1
- data/lib/json-schema/attributes/maximum.rb +1 -1
- data/lib/json-schema/attributes/maximum_inclusive.rb +1 -1
- data/lib/json-schema/attributes/maxitems.rb +1 -1
- data/lib/json-schema/attributes/maxlength.rb +1 -1
- data/lib/json-schema/attributes/minimum.rb +1 -1
- data/lib/json-schema/attributes/minimum_inclusive.rb +1 -1
- data/lib/json-schema/attributes/minitems.rb +1 -1
- data/lib/json-schema/attributes/minlength.rb +1 -1
- data/lib/json-schema/attributes/pattern.rb +1 -1
- data/lib/json-schema/attributes/properties.rb +1 -1
- data/lib/json-schema/attributes/properties_optional.rb +1 -1
- data/lib/json-schema/attributes/ref.rb +1 -1
- data/lib/json-schema/attributes/type.rb +29 -6
- data/lib/json-schema/attributes/uniqueitems.rb +1 -1
- data/lib/json-schema/schema.rb +3 -1
- data/lib/json-schema/validator.rb +41 -8
- data/test/test_extended_schema.rb +1 -1
- data/test/test_files.rb +14 -22
- data/test/test_full_validation.rb +110 -0
- metadata +2 -2
data/README.textile
CHANGED
@@ -5,7 +5,7 @@ module JSON
|
|
5
5
|
if data.is_a?(Array) && current_schema.schema['items'].is_a?(Array)
|
6
6
|
if current_schema.schema['additionalItems'] == false && current_schema.schema['items'].length != data.length
|
7
7
|
message = "The property '#{build_fragment(fragments)}' contains additional array elements outside of the schema when none are allowed"
|
8
|
-
validation_error(message, fragments, current_schema, options[:record_errors])
|
8
|
+
validation_error(message, fragments, current_schema, self, options[:record_errors])
|
9
9
|
elsif current_schema.schema['additionalItems'].is_a?(Hash)
|
10
10
|
schema = JSON::Schema.new(current_schema.schema['additionalItems'],current_schema.uri,validator)
|
11
11
|
data.each_with_index do |item,i|
|
@@ -23,7 +23,7 @@ module JSON
|
|
23
23
|
|
24
24
|
if current_schema.schema['additionalProperties'] == false && !extra_properties.empty?
|
25
25
|
message = "The property '#{build_fragment(fragments)}' contains additional properties #{extra_properties.inspect} outside of the schema when none are allowed"
|
26
|
-
validation_error(message, fragments, current_schema, options[:record_errors])
|
26
|
+
validation_error(message, fragments, current_schema, self, options[:record_errors])
|
27
27
|
elsif current_schema.schema['additionalProperties'].is_a?(Hash)
|
28
28
|
extra_properties.each do |key|
|
29
29
|
schema = JSON::Schema.new(current_schema.schema['additionalProperties'],current_schema.uri,validator)
|
@@ -8,13 +8,13 @@ module JSON
|
|
8
8
|
if dependency_value.is_a?(String)
|
9
9
|
if !data.has_key?(dependency_value)
|
10
10
|
message = "The property '#{build_fragment(fragments)}' has a property '#{property}' that depends on a missing property '#{dependency_value}'"
|
11
|
-
validation_error(message, fragments, current_schema, options[:record_errors])
|
11
|
+
validation_error(message, fragments, current_schema, self, options[:record_errors])
|
12
12
|
end
|
13
13
|
elsif dependency_value.is_a?(Array)
|
14
14
|
dependency_value.each do |value|
|
15
15
|
if !data.has_key?(value)
|
16
16
|
message = "The property '#{build_fragment(fragments)}' has a property '#{property}' that depends on a missing property '#{value}'"
|
17
|
-
validation_error(message, fragments, current_schema, options[:record_errors])
|
17
|
+
validation_error(message, fragments, current_schema, self, options[:record_errors])
|
18
18
|
end
|
19
19
|
end
|
20
20
|
else
|
@@ -7,7 +7,7 @@ module JSON
|
|
7
7
|
current_schema.schema['divisibleBy'] == 0.0 ||
|
8
8
|
(BigDecimal.new(data.to_s) % BigDecimal.new(current_schema.schema['divisibleBy'].to_s)).to_f != 0
|
9
9
|
message = "The property '#{build_fragment(fragments)}' was not divisible by #{current_schema.schema['divisibleBy']}"
|
10
|
-
validation_error(message, fragments, current_schema, options[:record_errors])
|
10
|
+
validation_error(message, fragments, current_schema, self, options[:record_errors])
|
11
11
|
end
|
12
12
|
end
|
13
13
|
end
|
@@ -8,26 +8,26 @@ module JSON
|
|
8
8
|
when 'date-time'
|
9
9
|
if data.is_a?(String)
|
10
10
|
error_message = "The property '#{build_fragment(fragments)}' must be a date/time in the ISO-8601 format of YYYY-MM-DDThh:mm:ssZ"
|
11
|
-
validation_error(error_message, fragments, current_schema, options[:record_errors]) and return if !data.is_a?(String)
|
11
|
+
validation_error(error_message, fragments, current_schema, self, options[:record_errors]) and return if !data.is_a?(String)
|
12
12
|
r = Regexp.new('^\d\d\d\d-\d\d-\d\dT(\d\d):(\d\d):(\d\d)Z$')
|
13
13
|
if (m = r.match(data))
|
14
14
|
parts = data.split("T")
|
15
15
|
begin
|
16
16
|
Date.parse(parts[0])
|
17
17
|
rescue Exception
|
18
|
-
validation_error(error_message, fragments, current_schema, options[:record_errors])
|
18
|
+
validation_error(error_message, fragments, current_schema, self, options[:record_errors])
|
19
19
|
return
|
20
20
|
end
|
21
21
|
begin
|
22
|
-
validation_error(error_message, fragments, current_schema, options[:record_errors]) and return if m[1].to_i > 23
|
23
|
-
validation_error(error_message, fragments, current_schema, options[:record_errors]) and return if m[2].to_i > 59
|
24
|
-
validation_error(error_message, fragments, current_schema, options[:record_errors]) and return if m[3].to_i > 59
|
22
|
+
validation_error(error_message, fragments, current_schema, self, options[:record_errors]) and return if m[1].to_i > 23
|
23
|
+
validation_error(error_message, fragments, current_schema, self, options[:record_errors]) and return if m[2].to_i > 59
|
24
|
+
validation_error(error_message, fragments, current_schema, self, options[:record_errors]) and return if m[3].to_i > 59
|
25
25
|
rescue Exception
|
26
|
-
validation_error(error_message, fragments, current_schema, options[:record_errors])
|
26
|
+
validation_error(error_message, fragments, current_schema, self, options[:record_errors])
|
27
27
|
return
|
28
28
|
end
|
29
29
|
else
|
30
|
-
validation_error(error_message, fragments, current_schema, options[:record_errors])
|
30
|
+
validation_error(error_message, fragments, current_schema, self, options[:record_errors])
|
31
31
|
return
|
32
32
|
end
|
33
33
|
end
|
@@ -36,17 +36,17 @@ module JSON
|
|
36
36
|
when 'date'
|
37
37
|
if data.is_a?(String)
|
38
38
|
error_message = "The property '#{build_fragment(fragments)}' must be a date in the format of YYYY-MM-DD"
|
39
|
-
validation_error(error_message, fragments, current_schema, options[:record_errors]) and return if !data.is_a?(String)
|
39
|
+
validation_error(error_message, fragments, current_schema, self, options[:record_errors]) and return if !data.is_a?(String)
|
40
40
|
r = Regexp.new('^\d\d\d\d-\d\d-\d\d$')
|
41
41
|
if (m = r.match(data))
|
42
42
|
begin
|
43
43
|
Date.parse(data)
|
44
44
|
rescue Exception
|
45
|
-
validation_error(error_message, fragments, current_schema, options[:record_errors])
|
45
|
+
validation_error(error_message, fragments, current_schema, self, options[:record_errors])
|
46
46
|
return
|
47
47
|
end
|
48
48
|
else
|
49
|
-
validation_error(error_message, fragments, current_schema, options[:record_errors])
|
49
|
+
validation_error(error_message, fragments, current_schema, self, options[:record_errors])
|
50
50
|
return
|
51
51
|
end
|
52
52
|
end
|
@@ -55,14 +55,14 @@ module JSON
|
|
55
55
|
when 'time'
|
56
56
|
if data.is_a?(String)
|
57
57
|
error_message = "The property '#{build_fragment(fragments)}' must be a time in the format of hh:mm:ss"
|
58
|
-
validation_error(error_message, fragments, current_schema, options[:record_errors]) and return if !data.is_a?(String)
|
58
|
+
validation_error(error_message, fragments, current_schema, self, options[:record_errors]) and return if !data.is_a?(String)
|
59
59
|
r = Regexp.new('^(\d\d):(\d\d):(\d\d)$')
|
60
60
|
if (m = r.match(data))
|
61
|
-
validation_error(error_message, fragments, current_schema, options[:record_errors]) and return if m[1].to_i > 23
|
62
|
-
validation_error(error_message, fragments, current_schema, options[:record_errors]) and return if m[2].to_i > 59
|
63
|
-
validation_error(error_message, fragments, current_schema, options[:record_errors]) and return if m[3].to_i > 59
|
61
|
+
validation_error(error_message, fragments, current_schema, self, options[:record_errors]) and return if m[1].to_i > 23
|
62
|
+
validation_error(error_message, fragments, current_schema, self, options[:record_errors]) and return if m[2].to_i > 59
|
63
|
+
validation_error(error_message, fragments, current_schema, self, options[:record_errors]) and return if m[3].to_i > 59
|
64
64
|
else
|
65
|
-
validation_error(error_message, fragments, current_schema, options[:record_errors])
|
65
|
+
validation_error(error_message, fragments, current_schema, self, options[:record_errors])
|
66
66
|
return
|
67
67
|
end
|
68
68
|
end
|
@@ -71,14 +71,14 @@ module JSON
|
|
71
71
|
when 'ip-address', 'ipv4'
|
72
72
|
if data.is_a?(String)
|
73
73
|
error_message = "The property '#{build_fragment(fragments)}' must be a valid IPv4 address"
|
74
|
-
validation_error(error_message, fragments, current_schema, options[:record_errors]) and return if !data.is_a?(String)
|
74
|
+
validation_error(error_message, fragments, current_schema, self, options[:record_errors]) and return if !data.is_a?(String)
|
75
75
|
r = Regexp.new('^(\d+){1,3}\.(\d+){1,3}\.(\d+){1,3}\.(\d+){1,3}$')
|
76
76
|
if (m = r.match(data))
|
77
77
|
1.upto(4) do |x|
|
78
|
-
validation_error(error_message, fragments, current_schema, options[:record_errors]) and return if m[x].to_i > 255
|
78
|
+
validation_error(error_message, fragments, current_schema, self, options[:record_errors]) and return if m[x].to_i > 255
|
79
79
|
end
|
80
80
|
else
|
81
|
-
validation_error(error_message, fragments, current_schema, options[:record_errors])
|
81
|
+
validation_error(error_message, fragments, current_schema, self, options[:record_errors])
|
82
82
|
return
|
83
83
|
end
|
84
84
|
end
|
@@ -87,22 +87,22 @@ module JSON
|
|
87
87
|
when 'ipv6'
|
88
88
|
if data.is_a?(String)
|
89
89
|
error_message = "The property '#{build_fragment(fragments)}' must be a valid IPv6 address"
|
90
|
-
validation_error(error_message, fragments, current_schema, options[:record_errors]) and return if !data.is_a?(String)
|
90
|
+
validation_error(error_message, fragments, current_schema, self, options[:record_errors]) and return if !data.is_a?(String)
|
91
91
|
r = Regexp.new('^[a-f0-9:]+$')
|
92
92
|
if (m = r.match(data))
|
93
93
|
# All characters are valid, now validate structure
|
94
94
|
parts = data.split(":")
|
95
|
-
validation_error(error_message, fragments, current_schema, options[:record_errors]) and return if parts.length > 8
|
95
|
+
validation_error(error_message, fragments, current_schema, self, options[:record_errors]) and return if parts.length > 8
|
96
96
|
condensed_zeros = false
|
97
97
|
parts.each do |part|
|
98
98
|
if part.length == 0
|
99
|
-
validation_error(error_message, fragments, current_schema, options[:record_errors]) and return if condensed_zeros
|
99
|
+
validation_error(error_message, fragments, current_schema, self, options[:record_errors]) and return if condensed_zeros
|
100
100
|
condensed_zeros = true
|
101
101
|
end
|
102
|
-
validation_error(error_message, fragments, current_schema, options[:record_errors]) and return if part.length > 4
|
102
|
+
validation_error(error_message, fragments, current_schema, self, options[:record_errors]) and return if part.length > 4
|
103
103
|
end
|
104
104
|
else
|
105
|
-
validation_error(error_message, fragments, current_schema, options[:record_errors])
|
105
|
+
validation_error(error_message, fragments, current_schema, self, options[:record_errors])
|
106
106
|
return
|
107
107
|
end
|
108
108
|
end
|
@@ -6,7 +6,7 @@ module JSON
|
|
6
6
|
s = data.to_s.split(".")[1]
|
7
7
|
if s && s.length > current_schema.schema['maxDecimal']
|
8
8
|
message = "The property '#{build_fragment(fragments)}' had more decimal places than the allowed #{current_schema.schema['maxDecimal']}"
|
9
|
-
validation_error(message, fragments, current_schema, options[:record_errors])
|
9
|
+
validation_error(message, fragments, current_schema, self, options[:record_errors])
|
10
10
|
end
|
11
11
|
end
|
12
12
|
end
|
@@ -6,7 +6,7 @@ module JSON
|
|
6
6
|
if (current_schema.schema['exclusiveMaximum'] ? data >= current_schema.schema['maximum'] : data > current_schema.schema['maximum'])
|
7
7
|
message = "The property '#{build_fragment(fragments)}' did not have a maximum value of #{current_schema.schema['maximum']}, "
|
8
8
|
message += current_schema.schema['exclusiveMaximum'] ? 'exclusively' : 'inclusively'
|
9
|
-
validation_error(message, fragments, current_schema, options[:record_errors])
|
9
|
+
validation_error(message, fragments, current_schema, self, options[:record_errors])
|
10
10
|
end
|
11
11
|
end
|
12
12
|
end
|
@@ -6,7 +6,7 @@ module JSON
|
|
6
6
|
if (current_schema.schema['maximumCanEqual'] == false ? data >= current_schema.schema['maximum'] : data > current_schema.schema['maximum'])
|
7
7
|
message = "The property '#{build_fragment(fragments)}' did not have a maximum value of #{current_schema.schema['maximum']}, "
|
8
8
|
message += current_schema.schema['exclusiveMaximum'] ? 'exclusively' : 'inclusively'
|
9
|
-
validation_error(message, fragments, current_schema, options[:record_errors])
|
9
|
+
validation_error(message, fragments, current_schema, self, options[:record_errors])
|
10
10
|
end
|
11
11
|
end
|
12
12
|
end
|
@@ -4,7 +4,7 @@ module JSON
|
|
4
4
|
def self.validate(current_schema, data, fragments, validator, options = {})
|
5
5
|
if data.is_a?(Array) && (data.compact.size > current_schema.schema['maxItems'])
|
6
6
|
message = "The property '#{build_fragment(fragments)}' did not contain a minimum number of items #{current_schema.schema['minItems']}"
|
7
|
-
validation_error(message, fragments, current_schema, options[:record_errors])
|
7
|
+
validation_error(message, fragments, current_schema, self, options[:record_errors])
|
8
8
|
end
|
9
9
|
end
|
10
10
|
end
|
@@ -5,7 +5,7 @@ module JSON
|
|
5
5
|
if data.is_a?(String)
|
6
6
|
if data.length > current_schema.schema['maxLength']
|
7
7
|
message = "The property '#{build_fragment(fragments)}' was not of a maximum string length of #{current_schema.schema['maxLength']}"
|
8
|
-
validation_error(message, fragments, current_schema, options[:record_errors])
|
8
|
+
validation_error(message, fragments, current_schema, self, options[:record_errors])
|
9
9
|
end
|
10
10
|
end
|
11
11
|
end
|
@@ -6,7 +6,7 @@ module JSON
|
|
6
6
|
if (current_schema.schema['exclusiveMinimum'] ? data <= current_schema.schema['minimum'] : data < current_schema.schema['minimum'])
|
7
7
|
message = "The property '#{build_fragment(fragments)}' did not have a minimum value of #{current_schema.schema['minimum']}, "
|
8
8
|
message += current_schema.schema['exclusiveMinimum'] ? 'exclusively' : 'inclusively'
|
9
|
-
validation_error(message, fragments, current_schema, options[:record_errors])
|
9
|
+
validation_error(message, fragments, current_schema, self, options[:record_errors])
|
10
10
|
end
|
11
11
|
end
|
12
12
|
end
|
@@ -6,7 +6,7 @@ module JSON
|
|
6
6
|
if (current_schema.schema['minimumCanEqual'] == false ? data <= current_schema.schema['minimum'] : data < current_schema.schema['minimum'])
|
7
7
|
message = "The property '#{build_fragment(fragments)}' did not have a minimum value of #{current_schema.schema['minimum']}, "
|
8
8
|
message += current_schema.schema['exclusiveMinimum'] ? 'exclusively' : 'inclusively'
|
9
|
-
validation_error(message, fragments, current_schema, options[:record_errors])
|
9
|
+
validation_error(message, fragments, current_schema, self, options[:record_errors])
|
10
10
|
end
|
11
11
|
end
|
12
12
|
end
|
@@ -4,7 +4,7 @@ module JSON
|
|
4
4
|
def self.validate(current_schema, data, fragments, validator, options = {})
|
5
5
|
if data.is_a?(Array) && (data.compact.size < current_schema.schema['minItems'])
|
6
6
|
message = "The property '#{build_fragment(fragments)}' did not contain a minimum number of items #{current_schema.schema['minItems']}"
|
7
|
-
validation_error(message, fragments, current_schema, options[:record_errors])
|
7
|
+
validation_error(message, fragments, current_schema, self, options[:record_errors])
|
8
8
|
end
|
9
9
|
end
|
10
10
|
end
|
@@ -5,7 +5,7 @@ module JSON
|
|
5
5
|
if data.is_a?(String)
|
6
6
|
if data.length < current_schema.schema['minLength']
|
7
7
|
message = "The property '#{build_fragment(fragments)}' was not of a minimum string length of #{current_schema.schema['minLength']}"
|
8
|
-
validation_error(message, fragments, current_schema, options[:record_errors])
|
8
|
+
validation_error(message, fragments, current_schema, self, options[:record_errors])
|
9
9
|
end
|
10
10
|
end
|
11
11
|
end
|
@@ -6,7 +6,7 @@ module JSON
|
|
6
6
|
r = Regexp.new(current_schema.schema['pattern'])
|
7
7
|
if (r.match(data)).nil?
|
8
8
|
message = "The property '#{build_fragment(fragments)}' did not match the regex '#{current_schema.schema['pattern']}'"
|
9
|
-
validation_error(message, fragments, current_schema, options[:record_errors])
|
9
|
+
validation_error(message, fragments, current_schema, self, options[:record_errors])
|
10
10
|
end
|
11
11
|
end
|
12
12
|
end
|
@@ -6,7 +6,7 @@ module JSON
|
|
6
6
|
current_schema.schema['properties'].each do |property,property_schema|
|
7
7
|
if (property_schema['required'] && !data.has_key?(property))
|
8
8
|
message = "The property '#{build_fragment(fragments)}' did not contain a required property of '#{property}'"
|
9
|
-
validation_error(message, fragments, current_schema, options[:record_errors])
|
9
|
+
validation_error(message, fragments, current_schema, self, options[:record_errors])
|
10
10
|
end
|
11
11
|
|
12
12
|
if data.has_key?(property)
|
@@ -6,7 +6,7 @@ module JSON
|
|
6
6
|
current_schema.schema['properties'].each do |property,property_schema|
|
7
7
|
if ((property_schema['optional'].nil? || property_schema['optional'] == false) && !data.has_key?(property))
|
8
8
|
message = "The property '#{build_fragment(fragments)}' did not contain a required property of '#{property}'"
|
9
|
-
validation_error(message, fragments, current_schema, options[:record_errors])
|
9
|
+
validation_error(message, fragments, current_schema, self, options[:record_errors])
|
10
10
|
end
|
11
11
|
|
12
12
|
if data.has_key?(property)
|
@@ -47,7 +47,7 @@ module JSON
|
|
47
47
|
schema.validate(data, fragments, options)
|
48
48
|
else
|
49
49
|
message = "The referenced schema '#{temp_uri.to_s}' cannot be found"
|
50
|
-
validation_error(message, fragments, current_schema, options[:record_errors])
|
50
|
+
validation_error(message, fragments, current_schema, self, options[:record_errors])
|
51
51
|
end
|
52
52
|
end
|
53
53
|
end
|
@@ -15,7 +15,10 @@ module JSON
|
|
15
15
|
union = false
|
16
16
|
end
|
17
17
|
valid = false
|
18
|
-
|
18
|
+
|
19
|
+
# Create an array to hold errors that are generated during union validation
|
20
|
+
union_errors = []
|
21
|
+
|
19
22
|
types.each do |type|
|
20
23
|
if type.is_a?(String)
|
21
24
|
case type
|
@@ -41,12 +44,24 @@ module JSON
|
|
41
44
|
elsif type.is_a?(Hash) && union
|
42
45
|
# Validate as a schema
|
43
46
|
schema = JSON::Schema.new(type,current_schema.uri,validator)
|
47
|
+
|
48
|
+
# We're going to add a little cruft here to try and maintain any validation errors that occur in this union type
|
49
|
+
# We'll handle this by keeping an error count before and after validation, extracting those errors and pushing them onto a union error
|
50
|
+
pre_validation_error_count = validation_errors.count
|
51
|
+
|
44
52
|
begin
|
45
53
|
schema.validate(data,fragments,options)
|
46
54
|
valid = true
|
47
55
|
rescue ValidationError
|
48
56
|
# We don't care that these schemas don't validate - we only care that one validated
|
49
57
|
end
|
58
|
+
|
59
|
+
diff = validation_errors.count - pre_validation_error_count
|
60
|
+
valid = false if diff > 0
|
61
|
+
while diff > 0
|
62
|
+
diff = diff - 1
|
63
|
+
union_errors.push(validation_errors.pop)
|
64
|
+
end
|
50
65
|
end
|
51
66
|
|
52
67
|
break if valid
|
@@ -57,13 +72,21 @@ module JSON
|
|
57
72
|
message = "The property '#{build_fragment(fragments)}' matched one or more of the following types:"
|
58
73
|
types.each {|type| message += type.is_a?(String) ? " #{type}," : " (schema)," }
|
59
74
|
message.chop!
|
60
|
-
validation_error(message, fragments, current_schema, options[:record_errors])
|
75
|
+
validation_error(message, fragments, current_schema, self, options[:record_errors])
|
61
76
|
end
|
62
77
|
elsif !valid
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
78
|
+
if union
|
79
|
+
message = "The property '#{build_fragment(fragments)}' of type #{data.class} did not match one or more of the following types:"
|
80
|
+
types.each {|type| message += type.is_a?(String) ? " #{type}," : " (schema)," }
|
81
|
+
message.chop!
|
82
|
+
validation_error(message, fragments, current_schema, self, options[:record_errors])
|
83
|
+
validation_errors.last.sub_errors = union_errors
|
84
|
+
else
|
85
|
+
message = "The property '#{build_fragment(fragments)}' of type #{data.class} did not match the following type:"
|
86
|
+
types.each {|type| message += type.is_a?(String) ? " #{type}," : " (schema)," }
|
87
|
+
message.chop!
|
88
|
+
validation_error(message, fragments, current_schema, self, options[:record_errors])
|
89
|
+
end
|
67
90
|
end
|
68
91
|
end
|
69
92
|
end
|
@@ -7,7 +7,7 @@ module JSON
|
|
7
7
|
dupes = d.uniq!
|
8
8
|
if dupes
|
9
9
|
message = "The property '#{build_fragment(fragments)}' contained duplicated array values"
|
10
|
-
validation_error(message, fragments, current_schema, options[:record_errors])
|
10
|
+
validation_error(message, fragments, current_schema, self, options[:record_errors])
|
11
11
|
end
|
12
12
|
end
|
13
13
|
end
|
data/lib/json-schema/schema.rb
CHANGED
@@ -9,14 +9,34 @@ module JSON
|
|
9
9
|
|
10
10
|
class Schema
|
11
11
|
class ValidationError < StandardError
|
12
|
-
|
12
|
+
attr_accessor :fragments, :schema, :failed_attribute, :sub_errors
|
13
13
|
|
14
|
-
def initialize(message, fragments, schema)
|
15
|
-
@fragments = fragments
|
14
|
+
def initialize(message, fragments, failed_attribute, schema)
|
15
|
+
@fragments = fragments.clone
|
16
16
|
@schema = schema
|
17
|
+
@sub_errors = []
|
18
|
+
@failed_attribute = failed_attribute
|
17
19
|
message = "#{message} in schema #{schema.uri}"
|
18
20
|
super(message)
|
19
21
|
end
|
22
|
+
|
23
|
+
def to_string
|
24
|
+
if @sub_errors.empty?
|
25
|
+
message
|
26
|
+
else
|
27
|
+
full_message = message + "\n The schema specific errors were:\n"
|
28
|
+
@sub_errors.each{|e| full_message = full_message + " - " + e.to_string + "\n"}
|
29
|
+
full_message
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_hash
|
34
|
+
base = {:schema => @schema.uri, :fragment => ::JSON::Schema::Attribute.build_fragment(fragments), :message => message, :failed_attribute => @failed_attribute.to_s.split(":").last.split("Attribute").first}
|
35
|
+
if !@sub_errors.empty?
|
36
|
+
base[:errors] = @sub_errors.map{|e| e.to_hash}
|
37
|
+
end
|
38
|
+
base
|
39
|
+
end
|
20
40
|
end
|
21
41
|
|
22
42
|
class SchemaError < StandardError
|
@@ -33,14 +53,18 @@ module JSON
|
|
33
53
|
"#/#{fragments.join('/')}"
|
34
54
|
end
|
35
55
|
|
36
|
-
def self.validation_error(message, fragments, current_schema, record_errors)
|
37
|
-
error = ValidationError.new(message, fragments, current_schema)
|
56
|
+
def self.validation_error(message, fragments, current_schema, failed_attribute, record_errors)
|
57
|
+
error = ValidationError.new(message, fragments, failed_attribute, current_schema)
|
38
58
|
if record_errors
|
39
|
-
::JSON::Validator.validation_error(error
|
59
|
+
::JSON::Validator.validation_error(error)
|
40
60
|
else
|
41
61
|
raise error
|
42
62
|
end
|
43
63
|
end
|
64
|
+
|
65
|
+
def self.validation_errors
|
66
|
+
::JSON::Validator.validation_errors
|
67
|
+
end
|
44
68
|
end
|
45
69
|
|
46
70
|
class Validator
|
@@ -84,7 +108,8 @@ module JSON
|
|
84
108
|
:list => false,
|
85
109
|
:version => nil,
|
86
110
|
:validate_schema => false,
|
87
|
-
:record_errors => false
|
111
|
+
:record_errors => false,
|
112
|
+
:errors_as_objects => false
|
88
113
|
}
|
89
114
|
@@validators = {}
|
90
115
|
@@default_validator = nil
|
@@ -148,7 +173,11 @@ module JSON
|
|
148
173
|
Validator.clear_errors
|
149
174
|
@base_schema.validate(@data,[],@validation_options)
|
150
175
|
Validator.clear_cache
|
151
|
-
|
176
|
+
if @options[:errors_as_objects]
|
177
|
+
@@errors.map{|e| e.to_hash}
|
178
|
+
else
|
179
|
+
@@errors.map{|e| e.to_string}
|
180
|
+
end
|
152
181
|
rescue JSON::Schema::ValidationError
|
153
182
|
Validator.clear_cache
|
154
183
|
raise $!
|
@@ -294,6 +323,10 @@ module JSON
|
|
294
323
|
def validation_error(error)
|
295
324
|
@@errors.push(error)
|
296
325
|
end
|
326
|
+
|
327
|
+
def validation_errors
|
328
|
+
@@errors
|
329
|
+
end
|
297
330
|
|
298
331
|
def schemas
|
299
332
|
@@schemas
|
@@ -5,7 +5,7 @@ class BitwiseAndAttribute < JSON::Schema::Attribute
|
|
5
5
|
def self.validate(current_schema, data, fragments, validator, options = {})
|
6
6
|
if data.is_a?(Integer) && data & current_schema.schema['bitwise-and'].to_i == 0
|
7
7
|
message = "The property '#{build_fragment(fragments)}' did not evaluate to true when bitwise-AND'd with #{current_schema.schema['bitwise-or']}"
|
8
|
-
raise JSON::Schema::ValidationError.new(message, fragments, current_schema)
|
8
|
+
raise JSON::Schema::ValidationError.new(message, fragments, self, current_schema)
|
9
9
|
end
|
10
10
|
end
|
11
11
|
end
|
data/test/test_files.rb
CHANGED
@@ -8,36 +8,28 @@ class JSONSchemaTest < Test::Unit::TestCase
|
|
8
8
|
#
|
9
9
|
|
10
10
|
def test_schema_from_file
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
assert(!JSON::Validator.validate(File.join(File.dirname(__FILE__),"schemas/good_schema_1.json"),data))
|
16
|
-
end
|
11
|
+
data = {"a" => 5}
|
12
|
+
assert(JSON::Validator.validate(File.join(File.dirname(__FILE__),"schemas/good_schema_1.json"),data))
|
13
|
+
data = {"a" => "bad"}
|
14
|
+
assert(!JSON::Validator.validate(File.join(File.dirname(__FILE__),"schemas/good_schema_1.json"),data))
|
17
15
|
end
|
18
16
|
|
19
17
|
def test_data_from_file
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
assert(!JSON::Validator.validate(schema,File.join(File.dirname(__FILE__),"data/bad_data_1.json")))
|
24
|
-
end
|
18
|
+
schema = {"type" => "object", "properties" => {"a" => {"type" => "integer"}}}
|
19
|
+
assert(JSON::Validator.validate(schema,File.join(File.dirname(__FILE__),"data/good_data_1.json")))
|
20
|
+
assert(!JSON::Validator.validate(schema,File.join(File.dirname(__FILE__),"data/bad_data_1.json")))
|
25
21
|
end
|
26
22
|
|
27
23
|
def test_both_from_file
|
28
|
-
|
29
|
-
|
30
|
-
assert(!JSON::Validator.validate(File.join(File.dirname(__FILE__),"schemas/good_schema_1.json"),File.join(File.dirname(__FILE__),"data/bad_data_1.json")))
|
31
|
-
end
|
24
|
+
assert(JSON::Validator.validate(File.join(File.dirname(__FILE__),"schemas/good_schema_1.json"),File.join(File.dirname(__FILE__),"data/good_data_1.json")))
|
25
|
+
assert(!JSON::Validator.validate(File.join(File.dirname(__FILE__),"schemas/good_schema_1.json"),File.join(File.dirname(__FILE__),"data/bad_data_1.json")))
|
32
26
|
end
|
33
27
|
|
34
28
|
def test_file_ref
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
assert(!JSON::Validator.validate(File.join(File.dirname(__FILE__),"schemas/good_schema_1.json"),data))
|
41
|
-
end
|
29
|
+
data = {"b" => {"a" => 5}}
|
30
|
+
assert(JSON::Validator.validate(File.join(File.dirname(__FILE__),"schemas/good_schema_2.json"),data))
|
31
|
+
|
32
|
+
data = {"b" => {"a" => "boo"}}
|
33
|
+
assert(!JSON::Validator.validate(File.join(File.dirname(__FILE__),"schemas/good_schema_1.json"),data))
|
42
34
|
end
|
43
35
|
end
|
@@ -35,4 +35,114 @@ class JSONFullValidation < Test::Unit::TestCase
|
|
35
35
|
errors = JSON::Validator.fully_validate(schema,data)
|
36
36
|
assert(errors.length == 2)
|
37
37
|
end
|
38
|
+
|
39
|
+
def test_full_validation_with_union_types
|
40
|
+
data = {"b" => 5}
|
41
|
+
schema = {
|
42
|
+
"$schema" => "http://json-schema.org/draft-03/schema#",
|
43
|
+
"type" => "object",
|
44
|
+
"properties" => {
|
45
|
+
"b" => {
|
46
|
+
"type" => ["null","integer"]
|
47
|
+
}
|
48
|
+
}
|
49
|
+
}
|
50
|
+
|
51
|
+
errors = JSON::Validator.fully_validate(schema,data)
|
52
|
+
assert(errors.empty?)
|
53
|
+
|
54
|
+
schema = {
|
55
|
+
"$schema" => "http://json-schema.org/draft-03/schema#",
|
56
|
+
"type" => "object",
|
57
|
+
"properties" => {
|
58
|
+
"b" => {
|
59
|
+
"type" => ["integer","null"]
|
60
|
+
}
|
61
|
+
}
|
62
|
+
}
|
63
|
+
|
64
|
+
errors = JSON::Validator.fully_validate(schema,data)
|
65
|
+
assert(errors.empty?)
|
66
|
+
|
67
|
+
data = {"b" => "a string"}
|
68
|
+
|
69
|
+
errors = JSON::Validator.fully_validate(schema,data)
|
70
|
+
assert(errors.length == 1)
|
71
|
+
|
72
|
+
schema = {
|
73
|
+
"$schema" => "http://json-schema.org/draft-03/schema#",
|
74
|
+
"type" => "object",
|
75
|
+
"properties" => {
|
76
|
+
"b" => {
|
77
|
+
"type" => [
|
78
|
+
{
|
79
|
+
"type" => "object",
|
80
|
+
"properties" => {
|
81
|
+
"c" => {"type" => "string"}
|
82
|
+
}
|
83
|
+
},
|
84
|
+
{
|
85
|
+
"type" => "object",
|
86
|
+
"properties" => {
|
87
|
+
"d" => {"type" => "integer"}
|
88
|
+
}
|
89
|
+
}
|
90
|
+
]
|
91
|
+
}
|
92
|
+
}
|
93
|
+
}
|
94
|
+
|
95
|
+
data = {"b" => {"c" => "taco"}}
|
96
|
+
|
97
|
+
errors = JSON::Validator.fully_validate(schema,data)
|
98
|
+
assert(errors.empty?)
|
99
|
+
|
100
|
+
data = {"b" => {"d" => 6}}
|
101
|
+
|
102
|
+
errors = JSON::Validator.fully_validate(schema,data)
|
103
|
+
assert(errors.empty?)
|
104
|
+
|
105
|
+
data = {"b" => {"c" => 6, "d" => "OH GOD"}}
|
106
|
+
|
107
|
+
errors = JSON::Validator.fully_validate(schema,data)
|
108
|
+
assert(errors.length == 1)
|
109
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
def test_full_validation_with_object_errors
|
113
|
+
data = {"b" => {"a" => 5}}
|
114
|
+
schema = {
|
115
|
+
"$schema" => "http://json-schema.org/draft-03/schema#",
|
116
|
+
"type" => "object",
|
117
|
+
"properties" => {
|
118
|
+
"b" => {
|
119
|
+
"required" => true
|
120
|
+
}
|
121
|
+
}
|
122
|
+
}
|
123
|
+
|
124
|
+
errors = JSON::Validator.fully_validate(schema,data,:errors_as_objects => true)
|
125
|
+
assert(errors.empty?)
|
126
|
+
|
127
|
+
data = {"c" => 5}
|
128
|
+
schema = {
|
129
|
+
"$schema" => "http://json-schema.org/draft-03/schema#",
|
130
|
+
"type" => "object",
|
131
|
+
"properties" => {
|
132
|
+
"b" => {
|
133
|
+
"required" => true
|
134
|
+
},
|
135
|
+
"c" => {
|
136
|
+
"type" => "string"
|
137
|
+
}
|
138
|
+
}
|
139
|
+
}
|
140
|
+
|
141
|
+
errors = JSON::Validator.fully_validate(schema,data,:errors_as_objects => true)
|
142
|
+
assert(errors.length == 2)
|
143
|
+
assert(errors[0][:failed_attribute] == "Properties")
|
144
|
+
assert(errors[0][:fragment] == "#/")
|
145
|
+
assert(errors[1][:failed_attribute] == "Type")
|
146
|
+
assert(errors[1][:fragment] == "#/c")
|
147
|
+
end
|
38
148
|
end
|