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.
@@ -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))