json-schema 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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