json-schema 1.0.3 → 1.0.4
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.
- 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
|