json-schema 1.2.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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-03" unless version
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-03"
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,@options[:version])
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
- schema = {"type" => "array", "items" => schema}
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
- schema = {"type" => "array", "items" => schema}
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
- schema = {"type" => "array", "items" => schema}
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))