json-schema 1.2.1 → 2.0.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.
- data/README.textile +21 -32
- data/lib/json-schema/attributes/allof.rb +37 -0
- data/lib/json-schema/attributes/anyof.rb +41 -0
- data/lib/json-schema/attributes/dependencies_v4.rb +20 -0
- data/lib/json-schema/attributes/maxproperties.rb +12 -0
- data/lib/json-schema/attributes/minproperties.rb +12 -0
- data/lib/json-schema/attributes/multipleof.rb +16 -0
- data/lib/json-schema/attributes/not.rb +22 -0
- data/lib/json-schema/attributes/oneof.rb +35 -0
- data/lib/json-schema/attributes/properties.rb +1 -26
- data/lib/json-schema/attributes/properties_v4.rb +23 -0
- data/lib/json-schema/attributes/required.rb +23 -0
- data/lib/json-schema/attributes/type.rb +6 -7
- data/lib/json-schema/attributes/type_v4.rb +54 -0
- data/lib/json-schema/schema.rb +6 -6
- data/lib/json-schema/validator.rb +28 -9
- data/lib/json-schema/validators/draft4.rb +45 -0
- data/resources/draft-04.json +174 -0
- data/test/{test_files.rb → test_files_v3.rb} +8 -8
- data/test/test_jsonschema_draft3.rb +42 -113
- data/test/test_jsonschema_draft4.rb +1227 -0
- metadata +24 -7
- checksums.yaml +0 -15
@@ -0,0 +1,54 @@
|
|
1
|
+
module JSON
|
2
|
+
class Schema
|
3
|
+
class TypeV4Attribute < Attribute
|
4
|
+
def self.validate(current_schema, data, fragments, processor, validator, options = {})
|
5
|
+
union = true
|
6
|
+
types = current_schema.schema['type']
|
7
|
+
if !types.is_a?(Array)
|
8
|
+
types = [types]
|
9
|
+
union = false
|
10
|
+
end
|
11
|
+
valid = false
|
12
|
+
|
13
|
+
# Create an array to hold errors that are generated during union validation
|
14
|
+
union_errors = []
|
15
|
+
|
16
|
+
types.each do |type|
|
17
|
+
valid = data_valid_for_type?(data, type)
|
18
|
+
break if valid
|
19
|
+
end
|
20
|
+
|
21
|
+
if !valid
|
22
|
+
if union
|
23
|
+
message = "The property '#{build_fragment(fragments)}' of type #{data.class} did not match one or more of the following types:"
|
24
|
+
types.each {|type| message += type.is_a?(String) ? " #{type}," : " (schema)," }
|
25
|
+
message.chop!
|
26
|
+
validation_error(processor, message, fragments, current_schema, self, options[:record_errors])
|
27
|
+
validation_errors(processor).last.sub_errors = union_errors
|
28
|
+
else
|
29
|
+
message = "The property '#{build_fragment(fragments)}' of type #{data.class} did not match the following type:"
|
30
|
+
types.each {|type| message += type.is_a?(String) ? " #{type}," : " (schema)," }
|
31
|
+
message.chop!
|
32
|
+
validation_error(processor, message, fragments, current_schema, self, options[:record_errors])
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
TYPE_CLASS_MAPPINGS = {
|
38
|
+
"string" => String,
|
39
|
+
"number" => Numeric,
|
40
|
+
"integer" => Integer,
|
41
|
+
"boolean" => [TrueClass, FalseClass],
|
42
|
+
"object" => Hash,
|
43
|
+
"array" => Array,
|
44
|
+
"null" => NilClass,
|
45
|
+
"any" => Object
|
46
|
+
}
|
47
|
+
|
48
|
+
def self.data_valid_for_type?(data, type)
|
49
|
+
valid_classes = TYPE_CLASS_MAPPINGS.fetch(type) { return true }
|
50
|
+
Array(valid_classes).any? { |c| data.is_a?(c) }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/lib/json-schema/schema.rb
CHANGED
@@ -4,11 +4,11 @@ module JSON
|
|
4
4
|
class Schema
|
5
5
|
|
6
6
|
attr_accessor :schema, :uri, :validator
|
7
|
-
|
7
|
+
|
8
8
|
def initialize(schema,uri,parent_validator=nil)
|
9
9
|
@schema = schema
|
10
10
|
@uri = uri
|
11
|
-
|
11
|
+
|
12
12
|
# If there is an ID on this schema, use it to generate the URI
|
13
13
|
if @schema['id']
|
14
14
|
temp_uri = URI.parse(@schema['id'])
|
@@ -19,7 +19,7 @@ module JSON
|
|
19
19
|
@uri = temp_uri
|
20
20
|
end
|
21
21
|
@uri.fragment = ''
|
22
|
-
|
22
|
+
|
23
23
|
# If there is a $schema on this schema, use it to determine which validator to use
|
24
24
|
if @schema['$schema']
|
25
25
|
u = URI.parse(@schema['$schema'])
|
@@ -31,13 +31,13 @@ module JSON
|
|
31
31
|
@validator = parent_validator
|
32
32
|
else
|
33
33
|
@validator = JSON::Validator.default_validator
|
34
|
-
end
|
34
|
+
end
|
35
35
|
end
|
36
|
-
|
36
|
+
|
37
37
|
def validate(data, fragments, processor, options = {})
|
38
38
|
@validator.validate(self, data, fragments, processor, options)
|
39
39
|
end
|
40
|
-
|
40
|
+
|
41
41
|
def base_uri
|
42
42
|
parts = @uri.to_s.split('/')
|
43
43
|
parts.pop
|
@@ -111,8 +111,7 @@ module JSON
|
|
111
111
|
:validate_schema => false,
|
112
112
|
:record_errors => false,
|
113
113
|
:errors_as_objects => false,
|
114
|
-
:insert_defaults => false
|
115
|
-
:strict => false
|
114
|
+
:insert_defaults => false
|
116
115
|
}
|
117
116
|
@@validators = {}
|
118
117
|
@@default_validator = nil
|
@@ -123,8 +122,10 @@ module JSON
|
|
123
122
|
|
124
123
|
def self.version_string_for(version)
|
125
124
|
# I'm not a fan of this, but it's quick and dirty to get it working for now
|
126
|
-
return "draft-
|
125
|
+
return "draft-04" unless version
|
127
126
|
case version.to_s
|
127
|
+
when "draft4"
|
128
|
+
"draft-04"
|
128
129
|
when "draft3"
|
129
130
|
"draft-03"
|
130
131
|
when "draft2"
|
@@ -145,7 +146,7 @@ module JSON
|
|
145
146
|
@errors = []
|
146
147
|
|
147
148
|
# I'm not a fan of this, but it's quick and dirty to get it working for now
|
148
|
-
version_string = "draft-
|
149
|
+
version_string = "draft-04"
|
149
150
|
if @options[:version]
|
150
151
|
version_string = @options[:version] = self.class.version_string_for(@options[:version])
|
151
152
|
u = URI.parse("http://json-schema.org/#{@options[:version]}/schema#")
|
@@ -155,7 +156,6 @@ module JSON
|
|
155
156
|
|
156
157
|
@validation_options = @options[:record_errors] ? {:record_errors => true} : {}
|
157
158
|
@validation_options[:insert_defaults] = true if @options[:insert_defaults]
|
158
|
-
@validation_options[:strict] = true if @options[:strict] == true
|
159
159
|
|
160
160
|
# validate the schema, if requested
|
161
161
|
if @options[:validate_schema]
|
@@ -248,6 +248,13 @@ module JSON
|
|
248
248
|
end
|
249
249
|
end
|
250
250
|
|
251
|
+
# "definitions" are schemas in V4
|
252
|
+
if parent_schema.schema["definitions"]
|
253
|
+
parent_schema.schema["definitions"].each do |k,v|
|
254
|
+
handle_schema(parent_schema, v)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
251
258
|
# All properties are schemas
|
252
259
|
if parent_schema.schema["properties"]
|
253
260
|
parent_schema.schema["properties"].each do |k,v|
|
@@ -286,7 +293,7 @@ module JSON
|
|
286
293
|
def handle_schema(parent_schema, obj)
|
287
294
|
if obj.is_a?(Hash)
|
288
295
|
schema_uri = parent_schema.uri.clone
|
289
|
-
schema = JSON::Schema.new(obj,schema_uri
|
296
|
+
schema = JSON::Schema.new(obj,schema_uri,parent_schema.validator)
|
290
297
|
if obj['id']
|
291
298
|
Validator.add_schema(schema)
|
292
299
|
end
|
@@ -504,7 +511,11 @@ module JSON
|
|
504
511
|
schema_uri = URI.parse(fake_uri(schema))
|
505
512
|
schema = JSON::Validator.parse(schema)
|
506
513
|
if @options[:list]
|
507
|
-
|
514
|
+
new_schema = {"type" => "array", "items" => schema}
|
515
|
+
if !schema["$schema"].nil?
|
516
|
+
new_schema["$schema"] = schema["$schema"]
|
517
|
+
end
|
518
|
+
schema = new_schema
|
508
519
|
end
|
509
520
|
schema = JSON::Schema.new(schema,schema_uri,@options[:version])
|
510
521
|
Validator.add_schema(schema)
|
@@ -522,7 +533,11 @@ module JSON
|
|
522
533
|
if Validator.schemas[schema_uri.to_s].nil?
|
523
534
|
schema = JSON::Validator.parse(open(schema_uri.to_s).read)
|
524
535
|
if @options[:list]
|
525
|
-
|
536
|
+
new_schema = {"type" => "array", "items" => schema}
|
537
|
+
if !schema["$schema"].nil?
|
538
|
+
new_schema["$schema"] = schema["$schema"]
|
539
|
+
end
|
540
|
+
schema = new_schema
|
526
541
|
end
|
527
542
|
schema = JSON::Schema.new(schema,schema_uri,@options[:version])
|
528
543
|
Validator.add_schema(schema)
|
@@ -532,7 +547,11 @@ module JSON
|
|
532
547
|
end
|
533
548
|
elsif schema.is_a?(Hash)
|
534
549
|
if @options[:list]
|
535
|
-
|
550
|
+
new_schema = {"type" => "array", "items" => schema}
|
551
|
+
if !schema["$schema"].nil?
|
552
|
+
new_schema["$schema"] = schema["$schema"]
|
553
|
+
end
|
554
|
+
schema = new_schema
|
536
555
|
end
|
537
556
|
schema_uri = URI.parse(fake_uri(serialize(schema)))
|
538
557
|
schema = JSON::Schema.new(schema,schema_uri,@options[:version])
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module JSON
|
2
|
+
class Schema
|
3
|
+
|
4
|
+
class Draft4 < Validator
|
5
|
+
def initialize
|
6
|
+
super
|
7
|
+
@attributes = {
|
8
|
+
"type" => JSON::Schema::TypeV4Attribute,
|
9
|
+
"allOf" => JSON::Schema::AllOfAttribute,
|
10
|
+
"anyOf" => JSON::Schema::AnyOfAttribute,
|
11
|
+
"oneOf" => JSON::Schema::OneOfAttribute,
|
12
|
+
"not" => JSON::Schema::NotAttribute,
|
13
|
+
"disallow" => JSON::Schema::DisallowAttribute,
|
14
|
+
"format" => JSON::Schema::FormatAttribute,
|
15
|
+
"maximum" => JSON::Schema::MaximumAttribute,
|
16
|
+
"minimum" => JSON::Schema::MinimumAttribute,
|
17
|
+
"minItems" => JSON::Schema::MinItemsAttribute,
|
18
|
+
"maxItems" => JSON::Schema::MaxItemsAttribute,
|
19
|
+
"minProperties" => JSON::Schema::MinPropertiesAttribute,
|
20
|
+
"maxProperties" => JSON::Schema::MaxPropertiesAttribute,
|
21
|
+
"uniqueItems" => JSON::Schema::UniqueItemsAttribute,
|
22
|
+
"minLength" => JSON::Schema::MinLengthAttribute,
|
23
|
+
"maxLength" => JSON::Schema::MaxLengthAttribute,
|
24
|
+
"multipleOf" => JSON::Schema::MultipleOfAttribute,
|
25
|
+
"enum" => JSON::Schema::EnumAttribute,
|
26
|
+
"properties" => JSON::Schema::PropertiesV4Attribute,
|
27
|
+
"required" => JSON::Schema::RequiredAttribute,
|
28
|
+
"pattern" => JSON::Schema::PatternAttribute,
|
29
|
+
"patternProperties" => JSON::Schema::PatternPropertiesAttribute,
|
30
|
+
"additionalProperties" => JSON::Schema::AdditionalPropertiesAttribute,
|
31
|
+
"items" => JSON::Schema::ItemsAttribute,
|
32
|
+
"additionalItems" => JSON::Schema::AdditionalItemsAttribute,
|
33
|
+
"dependencies" => JSON::Schema::DependenciesV4Attribute,
|
34
|
+
"extends" => JSON::Schema::ExtendsAttribute,
|
35
|
+
"$ref" => JSON::Schema::RefAttribute
|
36
|
+
}
|
37
|
+
@uri = URI.parse("http://json-schema.org/draft-04/schema#")
|
38
|
+
end
|
39
|
+
|
40
|
+
JSON::Validator.register_validator(self.new)
|
41
|
+
JSON::Validator.register_default_validator(self.new)
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
{
|
2
|
+
"$schema": "http://json-schema.org/draft-03/schema#",
|
3
|
+
"id": "http://json-schema.org/draft-03/schema#",
|
4
|
+
"type": "object",
|
5
|
+
|
6
|
+
"properties": {
|
7
|
+
"type": {
|
8
|
+
"type": [ "string", "array" ],
|
9
|
+
"items": {
|
10
|
+
"type": [ "string", { "$ref": "#" } ]
|
11
|
+
},
|
12
|
+
"uniqueItems": true,
|
13
|
+
"default": "any"
|
14
|
+
},
|
15
|
+
|
16
|
+
"properties": {
|
17
|
+
"type": "object",
|
18
|
+
"additionalProperties": { "$ref": "#" },
|
19
|
+
"default": {}
|
20
|
+
},
|
21
|
+
|
22
|
+
"patternProperties": {
|
23
|
+
"type": "object",
|
24
|
+
"additionalProperties": { "$ref": "#" },
|
25
|
+
"default": {}
|
26
|
+
},
|
27
|
+
|
28
|
+
"additionalProperties": {
|
29
|
+
"type": [ { "$ref": "#" }, "boolean" ],
|
30
|
+
"default": {}
|
31
|
+
},
|
32
|
+
|
33
|
+
"items": {
|
34
|
+
"type": [ { "$ref": "#" }, "array" ],
|
35
|
+
"items": { "$ref": "#" },
|
36
|
+
"default": {}
|
37
|
+
},
|
38
|
+
|
39
|
+
"additionalItems": {
|
40
|
+
"type": [ { "$ref": "#" }, "boolean" ],
|
41
|
+
"default": {}
|
42
|
+
},
|
43
|
+
|
44
|
+
"required": {
|
45
|
+
"type": "boolean",
|
46
|
+
"default": false
|
47
|
+
},
|
48
|
+
|
49
|
+
"dependencies": {
|
50
|
+
"type": "object",
|
51
|
+
"additionalProperties": {
|
52
|
+
"type": [ "string", "array", { "$ref": "#" } ],
|
53
|
+
"items": {
|
54
|
+
"type": "string"
|
55
|
+
}
|
56
|
+
},
|
57
|
+
"default": {}
|
58
|
+
},
|
59
|
+
|
60
|
+
"minimum": {
|
61
|
+
"type": "number"
|
62
|
+
},
|
63
|
+
|
64
|
+
"maximum": {
|
65
|
+
"type": "number"
|
66
|
+
},
|
67
|
+
|
68
|
+
"exclusiveMinimum": {
|
69
|
+
"type": "boolean",
|
70
|
+
"default": false
|
71
|
+
},
|
72
|
+
|
73
|
+
"exclusiveMaximum": {
|
74
|
+
"type": "boolean",
|
75
|
+
"default": false
|
76
|
+
},
|
77
|
+
|
78
|
+
"minItems": {
|
79
|
+
"type": "integer",
|
80
|
+
"minimum": 0,
|
81
|
+
"default": 0
|
82
|
+
},
|
83
|
+
|
84
|
+
"maxItems": {
|
85
|
+
"type": "integer",
|
86
|
+
"minimum": 0
|
87
|
+
},
|
88
|
+
|
89
|
+
"uniqueItems": {
|
90
|
+
"type": "boolean",
|
91
|
+
"default": false
|
92
|
+
},
|
93
|
+
|
94
|
+
"pattern": {
|
95
|
+
"type": "string",
|
96
|
+
"format": "regex"
|
97
|
+
},
|
98
|
+
|
99
|
+
"minLength": {
|
100
|
+
"type": "integer",
|
101
|
+
"minimum": 0,
|
102
|
+
"default": 0
|
103
|
+
},
|
104
|
+
|
105
|
+
"maxLength": {
|
106
|
+
"type": "integer"
|
107
|
+
},
|
108
|
+
|
109
|
+
"enum": {
|
110
|
+
"type": "array",
|
111
|
+
"minItems": 1,
|
112
|
+
"uniqueItems": true
|
113
|
+
},
|
114
|
+
|
115
|
+
"default": {
|
116
|
+
"type": "any"
|
117
|
+
},
|
118
|
+
|
119
|
+
"title": {
|
120
|
+
"type": "string"
|
121
|
+
},
|
122
|
+
|
123
|
+
"description": {
|
124
|
+
"type": "string"
|
125
|
+
},
|
126
|
+
|
127
|
+
"format": {
|
128
|
+
"type": "string"
|
129
|
+
},
|
130
|
+
|
131
|
+
"divisibleBy": {
|
132
|
+
"type": "number",
|
133
|
+
"minimum": 0,
|
134
|
+
"exclusiveMinimum": true,
|
135
|
+
"default": 1
|
136
|
+
},
|
137
|
+
|
138
|
+
"disallow": {
|
139
|
+
"type": [ "string", "array" ],
|
140
|
+
"items": {
|
141
|
+
"type": [ "string", { "$ref": "#" } ]
|
142
|
+
},
|
143
|
+
"uniqueItems": true
|
144
|
+
},
|
145
|
+
|
146
|
+
"extends": {
|
147
|
+
"type": [ { "$ref": "#" }, "array" ],
|
148
|
+
"items": { "$ref": "#" },
|
149
|
+
"default": {}
|
150
|
+
},
|
151
|
+
|
152
|
+
"id": {
|
153
|
+
"type": "string",
|
154
|
+
"format": "uri"
|
155
|
+
},
|
156
|
+
|
157
|
+
"$ref": {
|
158
|
+
"type": "string",
|
159
|
+
"format": "uri"
|
160
|
+
},
|
161
|
+
|
162
|
+
"$schema": {
|
163
|
+
"type": "string",
|
164
|
+
"format": "uri"
|
165
|
+
}
|
166
|
+
},
|
167
|
+
|
168
|
+
"dependencies": {
|
169
|
+
"exclusiveMinimum": "minimum",
|
170
|
+
"exclusiveMaximum": "maximum"
|
171
|
+
},
|
172
|
+
|
173
|
+
"default": {}
|
174
|
+
}
|
@@ -2,37 +2,37 @@ require 'test/unit'
|
|
2
2
|
require File.dirname(__FILE__) + '/../lib/json-schema'
|
3
3
|
|
4
4
|
class JSONSchemaTest < Test::Unit::TestCase
|
5
|
-
|
5
|
+
|
6
6
|
#
|
7
7
|
# These tests are ONLY run if there is an appropriate JSON backend parser available
|
8
8
|
#
|
9
|
-
|
9
|
+
|
10
10
|
def test_schema_from_file
|
11
11
|
data = {"a" => 5}
|
12
12
|
assert(JSON::Validator.validate(File.join(File.dirname(__FILE__),"schemas/good_schema_1.json"),data))
|
13
13
|
data = {"a" => "bad"}
|
14
14
|
assert(!JSON::Validator.validate(File.join(File.dirname(__FILE__),"schemas/good_schema_1.json"),data))
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
def test_data_from_file
|
18
|
-
schema = {"type" => "object", "properties" => {"a" => {"type" => "integer"}}}
|
18
|
+
schema = {"$schema" => "http://json-schema.org/draft-03/schema#","type" => "object", "properties" => {"a" => {"type" => "integer"}}}
|
19
19
|
assert(JSON::Validator.validate_uri(schema,File.join(File.dirname(__FILE__),"data/good_data_1.json")))
|
20
20
|
assert(!JSON::Validator.validate_uri(schema,File.join(File.dirname(__FILE__),"data/bad_data_1.json")))
|
21
21
|
end
|
22
|
-
|
22
|
+
|
23
23
|
def test_data_from_json
|
24
24
|
if JSON::Validator.json_backend != nil
|
25
|
-
schema = {"type" => "object", "properties" => {"a" => {"type" => "integer"}}}
|
25
|
+
schema = {"$schema" => "http://json-schema.org/draft-03/schema#","type" => "object", "properties" => {"a" => {"type" => "integer"}}}
|
26
26
|
assert(JSON::Validator.validate_json(schema, %Q({"a" : 5})))
|
27
27
|
assert(!JSON::Validator.validate_json(schema, %Q({"a" : "poop"})))
|
28
28
|
end
|
29
29
|
end
|
30
|
-
|
30
|
+
|
31
31
|
def test_both_from_file
|
32
32
|
assert(JSON::Validator.validate_uri(File.join(File.dirname(__FILE__),"schemas/good_schema_1.json"),File.join(File.dirname(__FILE__),"data/good_data_1.json")))
|
33
33
|
assert(!JSON::Validator.validate_uri(File.join(File.dirname(__FILE__),"schemas/good_schema_1.json"),File.join(File.dirname(__FILE__),"data/bad_data_1.json")))
|
34
34
|
end
|
35
|
-
|
35
|
+
|
36
36
|
def test_file_ref
|
37
37
|
data = {"b" => {"a" => 5}}
|
38
38
|
assert(JSON::Validator.validate(File.join(File.dirname(__FILE__),"schemas/good_schema_2.json"),data))
|