json-schema 0.1.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,89 @@
1
+ h1. Ruby JSON Schema Validator
2
+
3
+ This library is intended to provide Ruby with an interface for validating JSON objects against a JSON schema conforming to [[JSON Schema Draft 3|http://tools.ietf.org/html/draft-zyp-json-schema-03]]. The project originally started as a fork of [[Constellation's ruby-jsonschema|https://github.com/Constellation/ruby-jsonchema]] project, but differences in the JSON schema draft versions implemented as well as assumptions made by the ruby-jsonschema library forced this to become a new project.
4
+
5
+ This project is far from complete but is in a somewhat usable state...
6
+
7
+ h2. Usage
8
+
9
+ If downloading the git repo, build the gem and install it:
10
+
11
+ <pre>
12
+ $ rake package
13
+ $ gem install pkg/json-schema-0.1.0.gem
14
+ </pre>
15
+
16
+ <pre>
17
+ require 'rubygems'
18
+ require 'json-schema'
19
+
20
+ JSON::Validator.validate('schema.json', 'data.json')
21
+
22
+ schema = {
23
+ "properties" => {
24
+ "a" => {"type" => "integer"}
25
+ }
26
+ }
27
+
28
+ data = {
29
+ "a" => 5
30
+ }
31
+
32
+ JSON::Validator.validate(schema, data)
33
+ </pre>
34
+
35
+
36
+ h2. Currently implemented
37
+
38
+ The following core schema definitions are currently implemented:
39
+
40
+ * type
41
+ * properties
42
+ * patternProperties
43
+ * additionalProperties
44
+ * items
45
+ * additionalItems
46
+ * required
47
+ * dependencies
48
+ * minimum
49
+ * maximum
50
+ * exclusiveMinimum
51
+ * exclusiveMaximum
52
+ * minItems
53
+ * maxItems
54
+ * uniqueItems
55
+ * pattern
56
+ * minLength
57
+ * maxLength
58
+ * enum
59
+ * title
60
+ * description
61
+ * divisibleBy
62
+ * disallow
63
+ * extends
64
+ * id
65
+ * $ref (this implementation only follows slash-delimited fragment resolution)
66
+
67
+
68
+ h2. Not implemented
69
+
70
+ The following core schema definitions are not implemented:
71
+
72
+ * default - This library aims to solely be a validator and does not modify an object it is validating.
73
+ * $schema - Support for this will come later, possibly with the ability to validate against other JSON Schema draft versions
74
+
75
+ In addition, the following hyper schema attributes are not implemented at this time:
76
+
77
+ * links (not much to do with validation...)
78
+ * fragmentResolution (only handles default slash-delimited)
79
+ * readonly
80
+ * contentEncoding
81
+ * pathStart
82
+ * mediaType
83
+
84
+
85
+ h2. To Do
86
+
87
+ * Massive re-factoring - the whole validation process needs to be broken down into something a bit more manageable
88
+ * (Much) More testing
89
+ * Better interface to validator to allow for error messages in validation to propogate
@@ -0,0 +1,5 @@
1
+ $LOAD_PATH.unshift "#{File.dirname(__FILE__)}/json-schema"
2
+
3
+ require 'schema'
4
+ require 'validator'
5
+ require 'uri/file'
@@ -0,0 +1,39 @@
1
+ require 'rubygems'
2
+ require 'json'
3
+ require 'pathname'
4
+
5
+ module JSON
6
+ class Schema
7
+
8
+ attr_accessor :schema, :uri
9
+
10
+ def initialize(schema,uri)
11
+ @schema = schema
12
+ @uri = uri
13
+
14
+ # If there is an ID on this schema, use it to generate the URI
15
+ if @schema['id']
16
+ temp_uri = URI.parse(@schema['id'])
17
+ if temp_uri.relative?
18
+ uri.path = (Pathname.new(uri.path).parent + @schema['id']).cleanpath
19
+ temp_uri = uri
20
+ end
21
+ @uri = temp_uri
22
+ end
23
+ @uri.fragment = nil
24
+ end
25
+
26
+ def base_uri
27
+ parts = @uri.to_s.split('/')
28
+ parts.pop
29
+ parts.join('/') + '/'
30
+ end
31
+
32
+ def to_s
33
+ @schema.to_json
34
+ end
35
+
36
+
37
+ end
38
+ end
39
+
@@ -0,0 +1,32 @@
1
+ require 'uri'
2
+
3
+ module URI
4
+
5
+ # Ruby does not have built-in support for filesystem URIs, and definitely does not have built-in support for
6
+ # using open-uri with filesystem URIs
7
+ class File < Generic
8
+
9
+ COMPONENT = [
10
+ :scheme,
11
+ :path,
12
+ :fragment,
13
+ :host
14
+ ].freeze
15
+
16
+ def initialize(*arg)
17
+ arg[2] = ""
18
+ super(*arg)
19
+ end
20
+
21
+ def self.build(args)
22
+ tmp = Util::make_components_hash(self, args)
23
+ return super(tmp)
24
+ end
25
+
26
+ def open(*rest, &block)
27
+ ::File.open(self.path, *rest, &block)
28
+ end
29
+
30
+ @@schemes['FILE'] = File
31
+ end
32
+ end
@@ -0,0 +1,480 @@
1
+ require 'uri'
2
+ require 'open-uri'
3
+ require 'pathname'
4
+ require 'bigdecimal'
5
+
6
+ module JSON
7
+ class Validator
8
+
9
+ def initialize(schema_data, data)
10
+ @schemas = {}
11
+ @base_schema = initialize_schema(schema_data)
12
+ @data = initialize_data(data)
13
+ @schemas[@base_schema.uri.to_s] = @base_schema
14
+
15
+ build_schemas(@base_schema)
16
+ end
17
+
18
+
19
+ def validate()
20
+ validate_schema(@base_schema, @data)
21
+ end
22
+
23
+
24
+ def validate_schema(current_schema, data)
25
+ valid = true # Actually doing true/false on validation. Return at any point if invalid
26
+
27
+ # Check the type
28
+ if current_schema.schema['type']
29
+ valid = validate_type(current_schema.schema['type'],data,current_schema)
30
+ end
31
+ return valid if !valid
32
+ if current_schema.schema['disallow']
33
+ valid = !validate_type(current_schema.schema['disallow'],data,current_schema)
34
+ end
35
+ return valid if !valid
36
+
37
+ # Check the values
38
+ if current_schema.schema['minimum'] && data.is_a?(Numeric)
39
+ valid = current_schema.schema['exclusiveMinimum'] ? data > current_schema.schema['minimum'] : data >= current_schema.schema['minimum']
40
+ end
41
+ return valid if !valid
42
+
43
+ if current_schema.schema['maximum'] && data.is_a?(Numeric)
44
+ valid = current_schema.schema['exclusiveMaximum'] ? data < current_schema.schema['maximum'] : data <= current_schema.schema['maximum']
45
+ end
46
+ return valid if !valid
47
+
48
+ if current_schema.schema['minItems'] && data.is_a?(Array)
49
+ valid = data.nitems >= current_schema.schema['minItems']
50
+ end
51
+ return valid if !valid
52
+
53
+ if current_schema.schema['maxItems'] && data.is_a?(Array)
54
+ valid = data.nitems <= current_schema.schema['maxItems']
55
+ end
56
+ return valid if !valid
57
+
58
+ if current_schema.schema['uniqueItems'] && data.is_a?(Array)
59
+ d = data.clone
60
+ dupes = d.uniq!
61
+ valid = dupes.nil?
62
+ end
63
+ return valid if !valid
64
+
65
+ if current_schema.schema['pattern'] && data.is_a?(String)
66
+ r = Regexp.new(current_schema.schema['pattern'])
67
+ valid = !(r.match(data)).nil?
68
+ end
69
+ return valid if !valid
70
+
71
+ if current_schema.schema['minLength'] && data.is_a?(String)
72
+ valid = data.length >= current_schema.schema['minLength']
73
+ end
74
+ return valid if !valid
75
+
76
+ if current_schema.schema['maxLength'] && data.is_a?(String)
77
+ valid = data.length <= current_schema.schema['maxLength']
78
+ end
79
+ return valid if !valid
80
+
81
+ if current_schema.schema['divisibleBy'] && data.is_a?(Numeric)
82
+ if current_schema.schema['divisibleBy'] == 0 || current_schema.schema['divisibleBy'] == 0.0
83
+ return false
84
+ else
85
+ valid = (BigDecimal.new(data.to_s) % BigDecimal.new(current_schema.schema['divisibleBy'].to_s)).to_f == 0
86
+ end
87
+ end
88
+ return valid if !valid
89
+
90
+ if current_schema.schema['enum']
91
+ valid = current_schema.schema['enum'].include?(data)
92
+ end
93
+ return valid if !valid
94
+
95
+
96
+ if current_schema.schema['properties'] && data.is_a?(Hash)
97
+ current_schema.schema['properties'].each do |property,property_schema|
98
+ valid = false if (property_schema['required'] && !data.has_key?(property))
99
+
100
+ if data.has_key?(property)
101
+ schema = JSON::Schema.new(property_schema,current_schema.uri)
102
+ valid = validate_schema(schema, data[property])
103
+ end
104
+ return valid if !valid
105
+ end
106
+ end
107
+ return valid if !valid
108
+
109
+ if current_schema.schema['patternProperties'] && data.is_a?(Hash)
110
+ current_schema.schema['properties'].each do |property,property_schema|
111
+
112
+ r = Regexp.new(property)
113
+
114
+ # Check each key in the data hash to see if it matches the regex
115
+ data.each do |key,value|
116
+ if r.match(key)
117
+ schema = JSON::Schema.new(property_schema,current_schema.uri)
118
+ valid = validate_schema(schema, data[key])
119
+ end
120
+ return valid if !valid
121
+ end
122
+ end
123
+ end
124
+ return valid if !valid
125
+
126
+ if current_schema.schema['additionalProperties'] && data.is_a?(Hash)
127
+ extra_properties = data.keys
128
+
129
+ if current_schema.schema['properties']
130
+ extra_properties = extra_properties - current_schema.schema['properties'].keys
131
+ end
132
+
133
+ if current_schema.schema['patternProperties']
134
+ current_schema.schema['patternProperties'].each_key do |key|
135
+ r = Regexp.new(key)
136
+ extras_clone = extra_properties.clone
137
+ extras_clone.each do |prop|
138
+ if r.match(prop)
139
+ extra_properties = extra_properties - [prop]
140
+ end
141
+ end
142
+ end
143
+ end
144
+
145
+ if current_schema.schema['additionalProperties'] == false
146
+ valid = extra_properties.empty?
147
+ elsif current_schema.schema['additionalProperties'].is_a?(Hash)
148
+ data.each do |key,value|
149
+ schema = JSON::Schema.new(current_schema.schema['additionalProperties'],current_schema.uri)
150
+ valid = validate_schema(schema,value)
151
+ return valid if !valid
152
+ end
153
+ end
154
+ end
155
+ return valid if !valid
156
+
157
+ if current_schema.schema['items'] && data.is_a?(Array)
158
+ if current_schema.schema['items'].is_a?(Hash)
159
+ data.each do |item|
160
+ schema = JSON::Schema.new(current_schema.schema['items'],current_schema.uri)
161
+ valid = validate_schema(schema,item)
162
+ return valid if !valid
163
+ end
164
+ elsif current_schema.schema['items'].is_a?(Array)
165
+ current_schema.schema['items'].each_with_index do |item_schema,i|
166
+ schema = JSON::Schema.new(item_schema,current_schema.uri)
167
+ valid = validate_schema(schema,data[i])
168
+ return valid if !valid
169
+ end
170
+ end
171
+ end
172
+ return valid if !valid
173
+
174
+ if current_schema.schema['additionalItems'] && data.is_a?(Array) && current_schema.schema['items'].is_a?(array)
175
+ if current_schema.schema['additionalItems'] == false
176
+ return current_schema.schema['additionalItems'].length == data.length
177
+ elsif current_schema.schema['additionaItems'].is_a?(Hash)
178
+ data.each do |item|
179
+ schema = JSON::Schema.new(current_schema.schema['additionalItems'],current_schema.uri)
180
+ valid = validate_schema(schema, item)
181
+ return valid if !valid
182
+ end
183
+ end
184
+ end
185
+ return valid if !valid
186
+
187
+ if current_schema.schema['dependences'] && data.is_a?(Hash)
188
+ current_schema.schema['dependencies'].each do |property,dependency_value|
189
+ if data.has_key?(property)
190
+ if dependency_value.is_a?(String)
191
+ valid = data.has_key?(dependency_value)
192
+ elsif dependency_value.is_a?(Array)
193
+ dependency_value.each do |value|
194
+ valid = data.has_key?(value)
195
+ break if !valid
196
+ end
197
+ else
198
+ schema = JSON::Schema.new(dependency_value,current_schema.uri)
199
+ valid = validate_schema(schema, data)
200
+ end
201
+ return valid if !valid
202
+ end
203
+ end
204
+ end
205
+ return valid if !valid
206
+
207
+ # We're referencing another schema
208
+ if current_schema.schema['$ref']
209
+ temp_uri = URI.parse(current_schema.schema['$ref'])
210
+ if temp_uri.relative?
211
+ temp_uri = current_schema.uri.clone
212
+ temp_uri.path = (Pathname.new(current_schema.uri.path).parent + current_schema.schema['$ref'].split("#")[0]).cleanpath
213
+ temp_uri.fragment = current_schema.schema['$ref'].split("#")[1]
214
+ end
215
+ temp_uri.fragment = "" if temp_uri.fragment.nil?
216
+
217
+ # Grab the parent schema from the schema list
218
+ schema_key = temp_uri.to_s.split("#")[0]
219
+ ref_schema = @schemas[schema_key]
220
+
221
+ if ref_schema
222
+ # Perform fragment resolution to retrieve the appropriate level for the schema
223
+ target_schema = ref_schema.schema
224
+ fragments = temp_uri.fragment.split("/")
225
+ fragments.each do |fragment|
226
+ if fragment && fragment != ''
227
+ if target_schema.is_a?(Array)
228
+ target_schema = target_schema[fragment.to_i]
229
+ else
230
+ target_schema = target_schema[fragment]
231
+ end
232
+ end
233
+ end
234
+
235
+ # We have the schema finally, build it and validate!
236
+ schema = JSON::Schema.new(target_schema,temp_uri)
237
+ valid = validate_schema(schema, data)
238
+ end
239
+ end
240
+
241
+ if current_schema.schema['extends']
242
+ schemas = current_schema.schema['extends']
243
+ schemas = [schemas] if !schema.is_a?(Array)
244
+ schemas.each do |s|
245
+ schema = JSON::Schema.new(s,current_schema.uri)
246
+ valid = validate_schema(schema, item)
247
+ return valid if !valid
248
+ end
249
+ end
250
+
251
+ valid
252
+ end
253
+
254
+
255
+ def validate_type(types, data,current_schema)
256
+ union = true
257
+ if !types.is_a?(Array)
258
+ types = [types]
259
+ union = true
260
+ end
261
+ valid = false
262
+
263
+ types.each do |type|
264
+ if type.is_a?(String)
265
+ case type
266
+ when "string"
267
+ valid = data.is_a?(String)
268
+ when "number"
269
+ valid = data.is_a?(Numeric)
270
+ when "integer"
271
+ valid = data.is_a?(Integer)
272
+ when "boolean"
273
+ valid = (data.is_a?(TrueClass) || data.is_a?(FalseClass))
274
+ when "object"
275
+ valid = data.is_a?(Hash)
276
+ when "array"
277
+ valid = data.is_a?(Array)
278
+ when "null"
279
+ valid = data.is_a?(NilClass)
280
+ else
281
+ valid = true
282
+ end
283
+ elsif type.is_a?(Hash) && union
284
+ # Validate as a schema
285
+ schema = JSON::Schema.new(type,current_schema.uri)
286
+ valid = validate_schema(schema,data)
287
+ end
288
+
289
+ return valid if valid
290
+ end
291
+
292
+ valid
293
+ end
294
+
295
+ def load_ref_schema(parent_schema,ref)
296
+ uri = URI.parse(ref)
297
+ if uri.relative?
298
+ uri = parent_schema.uri.clone
299
+ uri.path = (Pathname.new(parent_schema.uri.path).parent + ref.split("#")[0]).cleanpath
300
+ uri.fragment = nil
301
+ end
302
+
303
+ if @schemas[uri.to_s].nil?
304
+ begin
305
+ schema = JSON::Schema.new(JSON.parse(open(uri.to_s).read), uri)
306
+ @schemas[uri.to_s] = schema
307
+ build_schemas(schema)
308
+ rescue
309
+ # Failures will occur when this URI cannot be referenced yet. Don't worry about it,
310
+ # the proper error will fall out if the ref isn't ever defined
311
+ end
312
+ end
313
+ end
314
+
315
+
316
+ # Build all schemas with IDs, mapping out the namespace
317
+ def build_schemas(parent_schema)
318
+ if parent_schema.schema["type"] && parent_schema.schema["type"].is_a?(Array) # If we're dealing with a Union type, there might be schemas a-brewin'
319
+ parent_schema.schema["type"].each_with_index do |type,i|
320
+ if type.is_a?(Hash)
321
+ if type['$ref']
322
+ load_ref_schema(parent_schema, type['$ref'])
323
+ else
324
+ schema_uri = parent_schema.uri.clone
325
+ schema = JSON::Schema.new(type,schema_uri)
326
+ if type['id']
327
+ @schemas[schema.uri.to_s] = schema
328
+ end
329
+ build_schemas(schema)
330
+ end
331
+ end
332
+ end
333
+ end
334
+
335
+ if parent_schema.schema["disallow"] && parent_schema.schema["disallow"].is_a?(Array) # If we're dealing with a Union type, there might be schemas a-brewin'
336
+ parent_schema.schema["disallow"].each_with_index do |type,i|
337
+ if type.is_a?(Hash)
338
+ if type['$ref']
339
+ load_ref_schema(parent_schema, type['$ref'])
340
+ else
341
+ type['id']
342
+ schema_uri = parent_schema.uri.clone
343
+ schema = JSON::Schema.new(type,schema_uri)
344
+ if type['id']
345
+ @schemas[schema.uri.to_s] = schema
346
+ end
347
+ build_schemas(schema)
348
+ end
349
+ end
350
+ end
351
+ end
352
+
353
+ if parent_schema.schema["properties"]
354
+ parent_schema.schema["properties"].each do |k,v|
355
+ if v['$ref']
356
+ load_ref_schema(parent_schema, v['$ref'])
357
+ else
358
+ schema_uri = parent_schema.uri.clone
359
+ schema = JSON::Schema.new(v,schema_uri)
360
+ if v['id']
361
+ @schemas[schema.uri.to_s] = schema
362
+ end
363
+ build_schemas(schema)
364
+ end
365
+ end
366
+ end
367
+
368
+ if parent_schema.schema["additionalProperties"].is_a?(Hash)
369
+ if parent_schema.schema["additionalProperties"]["$ref"]
370
+ load_ref_schema(parent_schema, parent_schema.schema["additionalProperties"]["$ref"])
371
+ else
372
+ schema_uri = parent_schema.uri.clone
373
+ schema = JSON::Schema.new(parent_schema.schema["additionalProperties"],schema_uri)
374
+ if parent_schema.schema["additionalProperties"]['id']
375
+ @schemas[schema.uri.to_s] = schema
376
+ end
377
+ build_schemas(schema)
378
+ end
379
+ end
380
+
381
+ if parent_schema.schema["items"]
382
+ items = parent_schema.schema["items"].clone
383
+ single = false
384
+ if !items.is_a?(Array)
385
+ items = [items]
386
+ single = true
387
+ end
388
+ items.each_with_index do |item,i|
389
+ if item['$ref']
390
+ load_ref_schema(parent_schema, item['$ref'])
391
+ else
392
+ schema_uri = parent_schema.uri.clone
393
+ schema = JSON::Schema.new(item,schema_uri)
394
+ if item['id']
395
+ @schemas[schema.uri.to_s] = schema
396
+ end
397
+ build_schemas(schema)
398
+ end
399
+ end
400
+ end
401
+
402
+ if parent_schema.schema["additionalItems"].is_a?(Hash)
403
+ if parent_schema.schema["additionalItems"]['$ref']
404
+ load_ref_schema(parent_schema, parent_schema.schema["additionalItems"]['$ref'])
405
+ else
406
+ schema_uri = parent_schema.uri.clone
407
+ schema = JSON::Schema.new(parent_schema.schema["additionalItems"],schema_uri)
408
+ if parent_schema.schema["additionalItems"]['id']
409
+ @schemas[schema.uri.to_s] = schema
410
+ end
411
+ build_schemas(schema)
412
+ end
413
+ end
414
+
415
+ if parent_schema.schema["dependencies"].is_a?(Hash)
416
+ if parent_schema.schema["dependencies"]["$ref"]
417
+ load_ref_schema(parent_schema, parent_schema.schema["dependencies"]['$ref'])
418
+ else
419
+ schema_uri = parent_schema.uri.clone
420
+ schema = JSON::Schema.new(parent_schema.schema["dependencies"],schema_uri)
421
+ if parent_schema.schema["dependencies"]['id']
422
+ @schemas[schema.uri.to_s] = schema
423
+ end
424
+ build_schemas(schema)
425
+ end
426
+ end
427
+ end
428
+
429
+
430
+ class << self
431
+ def validate(schema, data)
432
+ validator = JSON::Validator.new(schema, data)
433
+ validator.validate
434
+ end
435
+ end
436
+
437
+
438
+ private
439
+
440
+ def initialize_schema(schema)
441
+ schema_uri = URI.parse("file://#{Dir.pwd}/__base_schema__.json")
442
+ if schema.is_a?(String)
443
+ begin
444
+ schema = JSON.parse(schema)
445
+ rescue
446
+ begin
447
+ # Build a uri for it
448
+ schema_uri = URI.parse(schema)
449
+ schema_uri = URI.parse("file://#{Dir.pwd}/#{schema}") if schema_uri.relative?
450
+ schema = JSON.parse(open(schema_uri.to_s).read)
451
+ rescue
452
+ raise "Invalid schema: #{schema_uri.to_s}"
453
+ end
454
+ end
455
+ end
456
+
457
+ JSON::Schema.new(schema,schema_uri)
458
+ end
459
+
460
+
461
+ def initialize_data(data)
462
+ # Parse the data, if any
463
+ if data.is_a?(String)
464
+ begin
465
+ data = JSON.parse(data)
466
+ rescue
467
+ begin
468
+ json_uri = URI.parse(data)
469
+ json_uri = URI.parse("file://#{Dir.pwd}/#{data}") if json_uri.relative?
470
+ data = JSON.parse(open(json_uri.to_s).read)
471
+ rescue
472
+ raise "Invalid JSON: #{json_uri.to_s}"
473
+ end
474
+ end
475
+ end
476
+ data
477
+ end
478
+
479
+ end
480
+ end
@@ -0,0 +1,569 @@
1
+ require 'test/unit'
2
+ require File.dirname(__FILE__) + '/../lib/json-schema'
3
+
4
+ class JSONSchemaTest < Test::Unit::TestCase
5
+ def test_types
6
+ # Set up the default datatype
7
+ schema = {
8
+ "properties" => {
9
+ "a" => {}
10
+ }
11
+ }
12
+ data = {
13
+ "a" => nil
14
+ }
15
+
16
+ # Test integers
17
+ schema["properties"]["a"]["type"] = "integer"
18
+ data["a"] = 5
19
+ assert(JSON::Validator.validate(schema,data))
20
+
21
+ data["a"] = 5.2
22
+ assert(!JSON::Validator.validate(schema,data))
23
+
24
+ data['a'] = 'string'
25
+ assert(!JSON::Validator.validate(schema,data))
26
+
27
+ data['a'] = true
28
+ assert(!JSON::Validator.validate(schema,data))
29
+
30
+
31
+ # Test numbers
32
+ schema["properties"]["a"]["type"] = "number"
33
+ data["a"] = 5
34
+ assert(JSON::Validator.validate(schema,data))
35
+
36
+ data["a"] = 5.2
37
+ assert(JSON::Validator.validate(schema,data))
38
+
39
+ data['a'] = 'string'
40
+ assert(!JSON::Validator.validate(schema,data))
41
+
42
+ data['a'] = true
43
+ assert(!JSON::Validator.validate(schema,data))
44
+
45
+
46
+ # Test strings
47
+ schema["properties"]["a"]["type"] = "string"
48
+ data["a"] = 5
49
+ assert(!JSON::Validator.validate(schema,data))
50
+
51
+ data["a"] = 5.2
52
+ assert(!JSON::Validator.validate(schema,data))
53
+
54
+ data['a'] = 'string'
55
+ assert(JSON::Validator.validate(schema,data))
56
+
57
+ data['a'] = true
58
+ assert(!JSON::Validator.validate(schema,data))
59
+
60
+
61
+ # Test booleans
62
+ schema["properties"]["a"]["type"] = "boolean"
63
+ data["a"] = 5
64
+ assert(!JSON::Validator.validate(schema,data))
65
+
66
+ data["a"] = 5.2
67
+ assert(!JSON::Validator.validate(schema,data))
68
+
69
+ data['a'] = 'string'
70
+ assert(!JSON::Validator.validate(schema,data))
71
+
72
+ data['a'] = true
73
+ assert(JSON::Validator.validate(schema,data))
74
+
75
+ data['a'] = false
76
+ assert(JSON::Validator.validate(schema,data))
77
+
78
+
79
+ # Test object
80
+ schema["properties"]["a"]["type"] = "object"
81
+ data["a"] = {}
82
+ assert(JSON::Validator.validate(schema,data))
83
+
84
+ data["a"] = 5.2
85
+ assert(!JSON::Validator.validate(schema,data))
86
+
87
+ data['a'] = 'string'
88
+ assert(!JSON::Validator.validate(schema,data))
89
+
90
+ data['a'] = true
91
+ assert(!JSON::Validator.validate(schema,data))
92
+
93
+
94
+ # Test array
95
+ schema["properties"]["a"]["type"] = "array"
96
+ data["a"] = []
97
+ assert(JSON::Validator.validate(schema,data))
98
+
99
+ data["a"] = 5.2
100
+ assert(!JSON::Validator.validate(schema,data))
101
+
102
+ data['a'] = 'string'
103
+ assert(!JSON::Validator.validate(schema,data))
104
+
105
+ data['a'] = true
106
+ assert(!JSON::Validator.validate(schema,data))
107
+
108
+
109
+ # Test null
110
+ schema["properties"]["a"]["type"] = "null"
111
+ data["a"] = nil
112
+ assert(JSON::Validator.validate(schema,data))
113
+
114
+ data["a"] = 5.2
115
+ assert(!JSON::Validator.validate(schema,data))
116
+
117
+ data['a'] = 'string'
118
+ assert(!JSON::Validator.validate(schema,data))
119
+
120
+ data['a'] = true
121
+ assert(!JSON::Validator.validate(schema,data))
122
+
123
+
124
+ # Test any
125
+ schema["properties"]["a"]["type"] = "any"
126
+ data["a"] = 5
127
+ assert(JSON::Validator.validate(schema,data))
128
+
129
+ data["a"] = 5.2
130
+ assert(JSON::Validator.validate(schema,data))
131
+
132
+ data['a'] = 'string'
133
+ assert(JSON::Validator.validate(schema,data))
134
+
135
+ data['a'] = true
136
+ assert(JSON::Validator.validate(schema,data))
137
+
138
+
139
+ # Test a union type
140
+ schema["properties"]["a"]["type"] = ["integer","string"]
141
+ data["a"] = 5
142
+ assert(JSON::Validator.validate(schema,data))
143
+
144
+ data["a"] = 'boo'
145
+ assert(JSON::Validator.validate(schema,data))
146
+
147
+ data["a"] = false
148
+ assert(!JSON::Validator.validate(schema,data))
149
+ end
150
+
151
+
152
+
153
+ def test_required
154
+ # Set up the default datatype
155
+ schema = {
156
+ "properties" => {
157
+ "a" => {"required" => true}
158
+ }
159
+ }
160
+ data = {
161
+ }
162
+
163
+ assert(!JSON::Validator.validate(schema,data))
164
+ data['a'] = "Hello"
165
+ assert(JSON::Validator.validate(schema,data))
166
+ end
167
+
168
+
169
+
170
+ def test_minimum
171
+ # Set up the default datatype
172
+ schema = {
173
+ "properties" => {
174
+ "a" => {"minimum" => 5}
175
+ }
176
+ }
177
+
178
+ data = {
179
+ "a" => nil
180
+ }
181
+
182
+
183
+ # Test an integer
184
+ data["a"] = 5
185
+ assert(JSON::Validator.validate(schema,data))
186
+
187
+ data["a"] = 4
188
+ assert(!JSON::Validator.validate(schema,data))
189
+
190
+ # Test a float
191
+ data["a"] = 5.0
192
+ assert(JSON::Validator.validate(schema,data))
193
+
194
+ data["a"] = 4.9
195
+ assert(!JSON::Validator.validate(schema,data))
196
+
197
+ # Test a non-number
198
+ data["a"] = "a string"
199
+ assert(JSON::Validator.validate(schema,data))
200
+
201
+ # Test exclusiveMinimum
202
+ schema["properties"]["a"]["exclusiveMinimum"] = true
203
+
204
+ data["a"] = 6
205
+ assert(JSON::Validator.validate(schema,data))
206
+
207
+ data["a"] = 5
208
+ assert(!JSON::Validator.validate(schema,data))
209
+
210
+ # Test with float
211
+ data["a"] = 5.00000001
212
+ assert(JSON::Validator.validate(schema,data))
213
+
214
+ data["a"] = 5.0
215
+ assert(!JSON::Validator.validate(schema,data))
216
+ end
217
+
218
+
219
+
220
+ def test_maximum
221
+ # Set up the default datatype
222
+ schema = {
223
+ "properties" => {
224
+ "a" => {"maximum" => 5}
225
+ }
226
+ }
227
+
228
+ data = {
229
+ "a" => nil
230
+ }
231
+
232
+
233
+ # Test an integer
234
+ data["a"] = 5
235
+ assert(JSON::Validator.validate(schema,data))
236
+
237
+ data["a"] = 6
238
+ assert(!JSON::Validator.validate(schema,data))
239
+
240
+ # Test a float
241
+ data["a"] = 5.0
242
+ assert(JSON::Validator.validate(schema,data))
243
+
244
+ data["a"] = 5.1
245
+ assert(!JSON::Validator.validate(schema,data))
246
+
247
+ # Test a non-number
248
+ data["a"] = "a string"
249
+ assert(JSON::Validator.validate(schema,data))
250
+
251
+ # Test exclusiveMinimum
252
+ schema["properties"]["a"]["exclusiveMaximum"] = true
253
+
254
+ data["a"] = 4
255
+ assert(JSON::Validator.validate(schema,data))
256
+
257
+ data["a"] = 5
258
+ assert(!JSON::Validator.validate(schema,data))
259
+
260
+ # Test with float
261
+ data["a"] = 4.9999999
262
+ assert(JSON::Validator.validate(schema,data))
263
+
264
+ data["a"] = 5.0
265
+ assert(!JSON::Validator.validate(schema,data))
266
+ end
267
+
268
+
269
+ def test_min_items
270
+ # Set up the default datatype
271
+ schema = {
272
+ "properties" => {
273
+ "a" => {"minItems" => 1}
274
+ }
275
+ }
276
+
277
+ data = {
278
+ "a" => nil
279
+ }
280
+
281
+ # Test with an array
282
+ data["a"] = ["boo"]
283
+ assert(JSON::Validator.validate(schema,data))
284
+
285
+ data["a"] = []
286
+ assert(!JSON::Validator.validate(schema,data))
287
+
288
+ # Test with a non-array
289
+ data["a"] = "boo"
290
+ assert(JSON::Validator.validate(schema,data))
291
+ end
292
+
293
+
294
+
295
+ def test_max_items
296
+ # Set up the default datatype
297
+ schema = {
298
+ "properties" => {
299
+ "a" => {"maxItems" => 1}
300
+ }
301
+ }
302
+
303
+ data = {
304
+ "a" => nil
305
+ }
306
+
307
+ # Test with an array
308
+ data["a"] = ["boo"]
309
+ assert(JSON::Validator.validate(schema,data))
310
+
311
+ data["a"] = ["boo","taco"]
312
+ assert(!JSON::Validator.validate(schema,data))
313
+
314
+ # Test with a non-array
315
+ data["a"] = "boo"
316
+ assert(JSON::Validator.validate(schema,data))
317
+ end
318
+
319
+
320
+
321
+ def test_unique_items
322
+ # Set up the default datatype
323
+ schema = {
324
+ "properties" => {
325
+ "a" => {"uniqueItems" => true}
326
+ }
327
+ }
328
+
329
+ data = {
330
+ "a" => nil
331
+ }
332
+
333
+ # Test with nulls
334
+ data["a"] = [nil,5]
335
+ assert(JSON::Validator.validate(schema,data))
336
+
337
+ data["a"] = [nil,nil]
338
+ assert(!JSON::Validator.validate(schema,data))
339
+
340
+ # Test with booleans
341
+ data["a"] = [true,4]
342
+ assert(JSON::Validator.validate(schema,data))
343
+
344
+ data["a"] = [true,false]
345
+ assert(JSON::Validator.validate(schema,data))
346
+
347
+ data["a"] = [true,true]
348
+ assert(!JSON::Validator.validate(schema,data))
349
+
350
+ # Test with numbers
351
+ data["a"] = [4,true]
352
+ assert(JSON::Validator.validate(schema,data))
353
+
354
+ data["a"] = [4,4.1]
355
+ assert(JSON::Validator.validate(schema,data))
356
+
357
+ data["a"] = [4,4]
358
+ assert(!JSON::Validator.validate(schema,data))
359
+
360
+ # Test with strings
361
+ data["a"] = ['a',true]
362
+ assert(JSON::Validator.validate(schema,data))
363
+
364
+ data["a"] = ['a','ab']
365
+ assert(JSON::Validator.validate(schema,data))
366
+
367
+ data["a"] = ['a','a']
368
+ assert(!JSON::Validator.validate(schema,data))
369
+
370
+ # Test with arrays
371
+ data["a"] = [[1],true]
372
+ assert(JSON::Validator.validate(schema,data))
373
+
374
+ data["a"] = [[1,2],[1,3]]
375
+ assert(JSON::Validator.validate(schema,data))
376
+
377
+ data["a"] = [[1,2,3],[1,2,3]]
378
+ assert(!JSON::Validator.validate(schema,data))
379
+
380
+ # Test with objects
381
+ data["a"] = [{"a" => 1},true]
382
+ assert(JSON::Validator.validate(schema,data))
383
+
384
+ data["a"] = [{"a" => 1},{"a" => 2}]
385
+ assert(JSON::Validator.validate(schema,data))
386
+
387
+ data["a"] = [{"a" => 1, "b" => 2}, {"a" => 1, "b" => 2}]
388
+ assert(!JSON::Validator.validate(schema,data))
389
+ end
390
+
391
+
392
+ def test_pattern
393
+ # Set up the default datatype
394
+ schema = {
395
+ "properties" => {
396
+ "a" => {"pattern" => "\\d+ taco"}
397
+ }
398
+ }
399
+
400
+ data = {
401
+ "a" => nil
402
+ }
403
+
404
+ # Test strings
405
+ data["a"] = "156 taco bell"
406
+ assert(JSON::Validator.validate(schema,data))
407
+
408
+ data["a"] = "taco"
409
+ assert(!JSON::Validator.validate(schema,data))
410
+
411
+ # Test a non-string
412
+ data["a"] = 5
413
+ assert(JSON::Validator.validate(schema,data))
414
+ end
415
+
416
+
417
+ def test_min_length
418
+ # Set up the default datatype
419
+ schema = {
420
+ "properties" => {
421
+ "a" => {"minLength" => 1}
422
+ }
423
+ }
424
+
425
+ data = {
426
+ "a" => nil
427
+ }
428
+
429
+ # Try out strings
430
+ data["a"] = "t"
431
+ assert(JSON::Validator.validate(schema,data))
432
+
433
+ data["a"] = ""
434
+ assert(!JSON::Validator.validate(schema,data))
435
+
436
+ # Try out non-string
437
+ data["a"] = 5
438
+ assert(JSON::Validator.validate(schema,data))
439
+ end
440
+
441
+
442
+ def test_max_length
443
+ # Set up the default datatype
444
+ schema = {
445
+ "properties" => {
446
+ "a" => {"maxLength" => 1}
447
+ }
448
+ }
449
+
450
+ data = {
451
+ "a" => nil
452
+ }
453
+
454
+ # Try out strings
455
+ data["a"] = "t"
456
+ assert(JSON::Validator.validate(schema,data))
457
+
458
+ data["a"] = "tt"
459
+ assert(!JSON::Validator.validate(schema,data))
460
+
461
+ # Try out non-string
462
+ data["a"] = 5
463
+ assert(JSON::Validator.validate(schema,data))
464
+ end
465
+
466
+
467
+ def test_enum
468
+ # Set up the default datatype
469
+ schema = {
470
+ "properties" => {
471
+ "a" => {"enum" => [1,'boo',[1,2,3],{"a" => "b"}]}
472
+ }
473
+ }
474
+
475
+ data = {
476
+ "a" => nil
477
+ }
478
+
479
+ # Make sure all of the above are valid...
480
+ data["a"] = 1
481
+ assert(JSON::Validator.validate(schema,data))
482
+
483
+ data["a"] = 'boo'
484
+ assert(JSON::Validator.validate(schema,data))
485
+
486
+ data["a"] = [1,2,3]
487
+ assert(JSON::Validator.validate(schema,data))
488
+
489
+ data["a"] = {"a" => "b"}
490
+ assert(JSON::Validator.validate(schema,data))
491
+
492
+ # Test something that doesn't exist
493
+ data["a"] = 'taco'
494
+ assert(!JSON::Validator.validate(schema,data))
495
+
496
+ # Try it without the key
497
+ data = {}
498
+ assert(JSON::Validator.validate(schema,data))
499
+ end
500
+
501
+
502
+ def test_divisible_by
503
+ # Set up the default datatype
504
+ schema = {
505
+ "properties" => {
506
+ "a" => {"divisibleBy" => 1.1}
507
+ }
508
+ }
509
+
510
+ data = {
511
+ "a" => nil
512
+ }
513
+
514
+ data["a"] = 3.3
515
+ assert(JSON::Validator.validate(schema,data))
516
+
517
+ data["a"] = 3.4
518
+ assert(!JSON::Validator.validate(schema,data))
519
+
520
+ schema["properties"]["a"]["divisibleBy"] = 2.0
521
+
522
+ data["a"] = 4.0
523
+ assert(JSON::Validator.validate(schema,data))
524
+
525
+ data["a"] = 'boo'
526
+ assert(JSON::Validator.validate(schema,data))
527
+
528
+ data["a"] = 5
529
+ schema["properties"]["a"]["divisibleBy"] = 0
530
+ assert(!JSON::Validator.validate(schema,data))
531
+ end
532
+
533
+
534
+
535
+ def test_disallow
536
+ # Set up the default datatype
537
+ schema = {
538
+ "properties" => {
539
+ "a" => {"disallow" => "integer"}
540
+ }
541
+ }
542
+
543
+ data = {
544
+ "a" => nil
545
+ }
546
+
547
+
548
+ data["a"] = 'string'
549
+ assert(JSON::Validator.validate(schema,data))
550
+
551
+ data["a"] = 5
552
+ assert(!JSON::Validator.validate(schema,data))
553
+
554
+
555
+ schema["properties"]["a"]["disallow"] = ["integer","string"]
556
+ data["a"] = 'string'
557
+ assert(!JSON::Validator.validate(schema,data))
558
+
559
+ data["a"] = 5
560
+ assert(!JSON::Validator.validate(schema,data))
561
+
562
+ data["a"] = false
563
+ assert(JSON::Validator.validate(schema,data))
564
+
565
+ end
566
+
567
+
568
+ end
569
+
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: json-schema
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Kenny Hoxworth
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-11-24 00:00:00 -05:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: json
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ description: A Ruby JSON Schema Validator based on JSON Schema draft 03
36
+ email: hoxworth@gmail.com
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files:
42
+ - README.textile
43
+ files:
44
+ - lib/json-schema.rb
45
+ - lib/json-schema/schema.rb
46
+ - lib/json-schema/validator.rb
47
+ - lib/json-schema/uri/file.rb
48
+ - README.textile
49
+ - test/test_jsonschema.rb
50
+ has_rdoc: true
51
+ homepage: http://github.com/hoxworth/json-schema/tree/master
52
+ licenses: []
53
+
54
+ post_install_message:
55
+ rdoc_options: []
56
+
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ hash: 3
65
+ segments:
66
+ - 0
67
+ version: "0"
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ hash: 3
74
+ segments:
75
+ - 0
76
+ version: "0"
77
+ requirements: []
78
+
79
+ rubyforge_project:
80
+ rubygems_version: 1.3.7
81
+ signing_key:
82
+ specification_version: 3
83
+ summary: Ruby JSON Schema Validator
84
+ test_files:
85
+ - test/test_jsonschema.rb