jsonschema 1.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,94 @@
1
+ = Ruby/jsonschema
2
+
3
+ * http://github.com/Constellation/jsonchema
4
+
5
+ == DESCRIPTION:
6
+
7
+ json schema library ruby porting
8
+ from http://code.google.com/p/jsonschema/
9
+
10
+ == SYNOPSIS:
11
+ ruby code
12
+ require 'jsonschema'
13
+ schema = {
14
+ "type" => "object",
15
+ "properties" => {
16
+ "prop01" => {
17
+ "type" => "number",
18
+ "maximum" => 10
19
+ },
20
+ "prop02" => {
21
+ "type" => "integer",
22
+ "maximum" => 20
23
+ }
24
+ }
25
+ }
26
+ data = {
27
+ "prop01"=> 5,
28
+ "prop02"=> 10
29
+ }
30
+ JSON::Schema.validate(data, schema)
31
+
32
+ if you have json library
33
+ require 'json'
34
+ require 'jsonschema'
35
+ schema = File.open("path/to/schema.json", "rb"){|f| JSON.parse(f.read)}
36
+ data = File.open("path/to/data.json", "rb"){|f| JSON.parse(f.read)}
37
+ JSON::Schema.validate(data, schema)
38
+
39
+ == INSTALL:
40
+
41
+ gem source -a http://gemcutter.org
42
+ sudo gem install jsonschema
43
+
44
+ == LICENSE:
45
+
46
+ Ruby/jsonschema
47
+ (The MIT License)
48
+
49
+ Copyright (c) 2009 Constellation
50
+
51
+ Permission is hereby granted, free of charge, to any person obtaining
52
+ a copy of this software and associated documentation files (the
53
+ 'Software'), to deal in the Software without restriction, including
54
+ without limitation the rights to use, copy, modify, merge, publish,
55
+ distribute, sublicense, and/or sell copies of the Software, and to
56
+ permit persons to whom the Software is furnished to do so, subject to
57
+ the following conditions:
58
+
59
+ The above copyright notice and this permission notice shall be
60
+ included in all copies or substantial portions of the Software.
61
+
62
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
63
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
64
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
65
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
66
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
67
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
68
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
69
+
70
+
71
+ jsonschema
72
+ http://code.google.com/p/jsonschema/
73
+ (The MIT License)
74
+
75
+ Copyright (c) 2008 Ian Lewis, Yusuke Muraoka
76
+
77
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
78
+ this software and associated documentation files (the "Software"), to deal in
79
+ the Software without restriction, including without limitation the rights to
80
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
81
+ of the Software, and to permit persons to whom the Software is furnished to do
82
+ so, subject to the following conditions:
83
+
84
+ The above copyright notice and this permission notice shall be included in all
85
+ copies or substantial portions of the Software.
86
+
87
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
88
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
89
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
90
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
91
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
92
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
93
+ SOFTWARE.
94
+
@@ -0,0 +1,94 @@
1
+ # vim: fileencoding=utf-8
2
+ require 'rubygems'
3
+ require 'rake'
4
+ require 'rake/clean'
5
+ require 'rake/testtask'
6
+ require 'rake/packagetask'
7
+ require 'rake/gempackagetask'
8
+ require 'rake/rdoctask'
9
+ require 'rake/contrib/rubyforgepublisher'
10
+ require 'rake/contrib/sshpublisher'
11
+ require 'fileutils'
12
+ require 'lib/jsonschema'
13
+ include FileUtils
14
+
15
+ $version = JSON::Schema::VERSION
16
+ $readme = 'README.rdoc'
17
+ $rdoc_opts = %W(--main #{$readme} --charset utf-8 --line-numbers --inline-source)
18
+ $name = 'jsonschema'
19
+ $github_name = 'ruby-jsonschema'
20
+ $summary = 'json schema library ruby porting from http://code.google.com/p/jsonschema/'
21
+ $author = 'Constellation'
22
+ $email = 'utatane.tea@gmail.com'
23
+ $page = 'http://github.com/Constellation/ruby-jsonchema/tree/master'
24
+ #$exec = %W(jsonschema)
25
+ $rubyforge_project = 'jsonschema'
26
+
27
+
28
+ task :default => [:test]
29
+ task :package => [:clean]
30
+
31
+ Rake::TestTask.new("test") do |t|
32
+ t.libs << "test"
33
+ t.pattern = "test/**/*_test.rb"
34
+ t.verbose = true
35
+ end
36
+
37
+ spec = Gem::Specification.new do |s|
38
+ s.name = $name
39
+ s.version = $version
40
+ s.platform = Gem::Platform::RUBY
41
+ s.has_rdoc = true
42
+ s.extra_rdoc_files = [$readme]
43
+ s.rdoc_options += $rdoc_opts
44
+ s.summary = $summary
45
+ s.description = $summary
46
+ s.author = $author
47
+ s.email = $email
48
+ s.homepage = $page
49
+ s.executables = $exec
50
+ s.rubyforge_project = $rubyforge_project
51
+ # s.bindir = 'bin'
52
+ s.require_path = 'lib'
53
+ s.test_files = Dir["test/*_test.rb"]
54
+ # {
55
+ # }.each do |dep, ver|
56
+ # s.add_dependency(dep, ver)
57
+ # end
58
+ s.files = %w(README.rdoc Rakefile) + Dir["{bin,test,lib}/**/*"]
59
+ end
60
+
61
+ Rake::GemPackageTask.new(spec) do |p|
62
+ p.need_tar = true
63
+ p.gem_spec = spec
64
+ end
65
+
66
+ Rake::RDocTask.new do |rdoc|
67
+ rdoc.rdoc_dir = 'doc'
68
+ rdoc.options += $rdoc_opts
69
+ # rdoc.template = 'resh'
70
+ rdoc.rdoc_files.include("README.rdoc", "lib/**/*.rb", "ext/**/*.c")
71
+ end
72
+
73
+ desc "gem spec"
74
+ task :gemspec do
75
+ File.open("#{$github_name}.gemspec", "wb") do |f|
76
+ f << spec.to_ruby
77
+ end
78
+ end
79
+
80
+ desc "gem build"
81
+ task :build => [:gemspec] do
82
+ sh "gem build #{$github_name}.gemspec"
83
+ end
84
+
85
+ desc "gem install"
86
+ task :install => [:build] do
87
+ sh "sudo gem install #{$name}-#{$version}.gem --local"
88
+ end
89
+
90
+ desc "gem uninstall"
91
+ task :uninstall do
92
+ sh "sudo gem uninstall #{$name}"
93
+ end
94
+ # vim: syntax=ruby
@@ -0,0 +1,432 @@
1
+ # vim: fileencoding=utf-8
2
+
3
+ module JSON
4
+ class Schema
5
+ VERSION = '1.0.0'
6
+ class ValueError < Exception;end
7
+ TypesMap = {
8
+ "string" => String,
9
+ "integer" => Integer,
10
+ "number" => [Integer, Float],
11
+ "boolean" => [TrueClass, FalseClass],
12
+ "object" => Hash,
13
+ "array" => Array,
14
+ "null" => NilClass,
15
+ "any" => nil
16
+ }
17
+ TypesList = [String, Integer, Float, TrueClass, FalseClass, Hash, Array, NilClass]
18
+ DefaultSchema = {
19
+ "id" => nil,
20
+ "type" => nil,
21
+ "properties" => nil,
22
+ "items" => nil,
23
+ "optional" => false,
24
+ "additionalProperties" => nil,
25
+ "requires" => nil,
26
+ "identity" => nil,
27
+ "minimum" => nil,
28
+ "maximum" => nil,
29
+ "minItems" => nil,
30
+ "maxItems" => nil,
31
+ "pattern" => nil,
32
+ "maxLength" => nil,
33
+ "minLength" => nil,
34
+ "enum" => nil,
35
+ "options" => nil,
36
+ "readonly" => nil,
37
+ "title" => nil,
38
+ "description" => nil,
39
+ "format" => nil,
40
+ "default" => nil,
41
+ "transient" => nil,
42
+ "maxDecimal" => nil,
43
+ "hidden" => nil,
44
+ "disallow" => nil,
45
+ "extends" => nil
46
+ }
47
+ def initialize interactive=true
48
+ @interactive = interactive
49
+ @refmap = {}
50
+ end
51
+
52
+ def validate data, schema
53
+ @refmap = {
54
+ '$' => schema
55
+ }
56
+ _validate(data, schema)
57
+ end
58
+
59
+ private
60
+ def validate_id x, fieldname, schema, id=nil
61
+ unless id.nil?
62
+ if id == '$'
63
+ raise ValueError, "Reference id for field '#{fieldname}' cannot equal '$'"
64
+ end
65
+ @refmap[id] = schema
66
+ end
67
+ return x
68
+ end
69
+
70
+ def validate_type x, fieldname, schema, fieldtype=nil
71
+ converted_fieldtype = convert_type(fieldtype)
72
+ fieldexists = true
73
+ begin
74
+ val = x.fetch(fieldname)
75
+ rescue IndexError
76
+ fieldexists = false
77
+ ensure
78
+ val = x[fieldname]
79
+ end
80
+ if converted_fieldtype && fieldexists
81
+ if converted_fieldtype.kind_of? Array
82
+ datavalid = false
83
+ converted_fieldtype.each do |type|
84
+ begin
85
+ validate_type(x, fieldname, type, type)
86
+ datavalid = true
87
+ break
88
+ rescue ValueError
89
+ next
90
+ end
91
+ end
92
+ unless datavalid
93
+ raise ValueError, "Value #{val} for field '#{fieldname}' is not of type #{fieldtype}"
94
+ end
95
+ elsif converted_fieldtype.kind_of? Hash
96
+ begin
97
+ __validate(fieldname, x, converted_fieldtype)
98
+ rescue ValueError => e
99
+ raise e
100
+ end
101
+ else
102
+ unless val.kind_of? converted_fieldtype
103
+ raise ValueError, "Value #{val} for field '#{fieldname}' is not of type #{fieldtype}"
104
+ end
105
+ end
106
+ end
107
+ return x
108
+ end
109
+
110
+ def validate_properties x, fieldname, schema, properties=nil
111
+ if !properties.nil? && x[fieldname]
112
+ value = x[fieldname]
113
+ if value
114
+ if value.kind_of? Hash
115
+ if properties.kind_of? Hash
116
+ properties.each do |key, val|
117
+ __validate(key, value, val)
118
+ end
119
+ else
120
+ raise ValueError, "Properties definition of field '#{fieldname}' is not an object"
121
+ end
122
+ end
123
+ end
124
+ end
125
+ return x
126
+ end
127
+
128
+ def validate_items x, fieldname, schema, items=nil
129
+ if !items.nil? && x[fieldname]
130
+ value = x[fieldname]
131
+ unless value.nil?
132
+ if value.kind_of? Array
133
+ if items.kind_of? Array
134
+ if items.size == value.size
135
+ items.each_with_index do |item, index|
136
+ begin
137
+ validate(value[index], item)
138
+ rescue ValueError => e
139
+ raise ValueError, "Failed to validate field '#{fieldname}' list schema: #{e.message}"
140
+ end
141
+ end
142
+ else
143
+ raise ValueError, "Length of list #{value} for field '#{fieldname}' is not equal to length of schema list"
144
+ end
145
+ elsif items.kind_of? Hash
146
+ value.each do |val|
147
+ begin
148
+ _validate(val, items)
149
+ rescue ValueError => e
150
+ raise ValueError, "Failed to validate field '#{fieldname}' list schema: #{e.message}"
151
+ end
152
+ end
153
+ else
154
+ raise ValueError, "Properties definition of field '#{fieldname}' is not a list or an object"
155
+ end
156
+ end
157
+ end
158
+ end
159
+ return x
160
+ end
161
+
162
+ def validate_optional x, fieldname, schema, optional=false
163
+ if !x.include?(fieldname) && !optional
164
+ raise ValueError, "Required field '#{fieldname}' is missing"
165
+ end
166
+ return x
167
+ end
168
+
169
+ def validate_additionalProperties x, fieldname, schema, additional_properties=nil
170
+ unless additional_properties.nil?
171
+ if additional_properties.kind_of? TrueClass
172
+ return x
173
+ end
174
+ value = x[fieldname]
175
+ if additional_properties.kind_of?(Hash) || additional_properties.kind_of?(FalseClass)
176
+ properties = schema["properties"]
177
+ unless properties
178
+ properties = {}
179
+ end
180
+ value.keys.each do |key|
181
+ unless properties.include? key
182
+ if additional_properties.kind_of? FalseClass
183
+ raise ValueError, "Additional properties not defined by 'properties' are not allowed in field '#{fieldname}'"
184
+ else
185
+ __validate(key, value, additional_properties)
186
+ end
187
+ end
188
+ end
189
+ else
190
+ raise ValueError, "additionalProperties schema definition for field '#{fieldname}' is not an object"
191
+ end
192
+ end
193
+ return x
194
+ end
195
+
196
+ def validate_requires x, fieldname, schema, requires=nil
197
+ if x[fieldname] && !requires.nil?
198
+ unless x[requires]
199
+ raise ValueError, "Field '#{requires}' is required by field '#{fieldname}'"
200
+ end
201
+ end
202
+ return x
203
+ end
204
+
205
+ def validate_identity x, fieldname, schema, unique=false
206
+ return x
207
+ end
208
+
209
+ def validate_minimum x, fieldname, schema, minimum=nil
210
+ if !minimum.nil? && x[fieldname]
211
+ value = x[fieldname]
212
+ if value
213
+ if (value.kind_of?(Integer) || value.kind_of?(Float)) && value < minimum
214
+ raise ValueError, "Value #{value} for field '#{fieldname}' is less than minimum value: #{minimum}"
215
+ elsif value.kind_of?(Array) && value.size < minimum
216
+ raise ValueError, "Value #{value} for field '#{fieldname}' has fewer values than the minimum: #{minimum}"
217
+ end
218
+ end
219
+ end
220
+ return x
221
+ end
222
+
223
+ def validate_maximum x, fieldname, schema, maximum=nil
224
+ if !maximum.nil? && x[fieldname]
225
+ value = x[fieldname]
226
+ if value
227
+ if (value.kind_of?(Integer) || value.kind_of?(Float)) && value > maximum
228
+ raise ValueError, "Value #{value} for field '#{fieldname}' is greater than maximum value: #{maximum}"
229
+ elsif value.kind_of?(Array) && value.size > maximum
230
+ raise ValueError, "Value #{value} for field '#{fieldname}' has more values than the maximum: #{maximum}"
231
+ end
232
+ end
233
+ end
234
+ return x
235
+ end
236
+
237
+ def validate_minItems x, fieldname, schema, minitems=nil
238
+ if !minitems.nil? && x[fieldname]
239
+ value = x[fieldname]
240
+ if value
241
+ if value.kind_of?(Array) && value.size < minitems
242
+ raise ValueError, "Value #{value} for field '#{fieldname}' must have a minimum of #{minitems} items"
243
+ end
244
+ end
245
+ end
246
+ return x
247
+ end
248
+
249
+ def validate_maxItems x, fieldname, schema, maxitems=nil
250
+ if !maxitems.nil? && x[fieldname]
251
+ value = x[fieldname]
252
+ if value
253
+ if value.kind_of?(Array) && value.size > maxitems
254
+ raise ValueError, "Value #{value} for field '#{fieldname}' must have a maximum of #{maxitems} items"
255
+ end
256
+ end
257
+ end
258
+ return x
259
+ end
260
+
261
+ def validate_pattern x, fieldname, schema, pattern=nil
262
+ value = x[fieldname]
263
+ if !pattern.nil? && value && value.kind_of?(String)
264
+ p = Regexp.new(pattern)
265
+ if !p.match(value)
266
+ raise ValueError, "Value #{value} for field '#{fieldname}' does not match regular expression '#{pattern}'"
267
+ end
268
+ end
269
+ return x
270
+ end
271
+
272
+ def validate_maxLength x, fieldname, schema, length=nil
273
+ value = x[fieldname]
274
+ if !length.nil? && value && value.kind_of?(String)
275
+ # string length => 正規表現で分割して計測
276
+ if value.split(//).size > length
277
+ raise ValueError, "Length of value #{value} for field '#{fieldname}' must be less than or equal to #{length}"
278
+ end
279
+ end
280
+ return x
281
+ end
282
+
283
+ def validate_minLength x, fieldname, schema, length=nil
284
+ value = x[fieldname]
285
+ if !length.nil? && value && value.kind_of?(String)
286
+ if value.split(//).size < length
287
+ raise ValueError, "Length of value #{value} for field '#{fieldname}' must be more than or equal to #{length}"
288
+ end
289
+ end
290
+ return x
291
+ end
292
+
293
+ def validate_enum x, fieldname, schema, options=nil
294
+ value = x[fieldname]
295
+ if !options.nil? && value
296
+ unless options.kind_of? Array
297
+ raise ValueError, "Enumeration #{options} for field '#{fieldname}' is not a list type"
298
+ end
299
+ unless options.include? value
300
+ raise ValueError, "Value #{value} for field '#{fieldname}' is not in the enumeration: #{options}"
301
+ end
302
+ end
303
+ return x
304
+ end
305
+
306
+ def validate_options x, fieldname, schema, options=nil
307
+ return x
308
+ end
309
+
310
+ def validate_readonly x, fieldname, schema, readonly=false
311
+ return x
312
+ end
313
+
314
+ def validate_title x, fieldname, schema, title=nil
315
+ if !title.nil? && !title.kind_of?(String)
316
+ raise ValueError, "The title for field '#{fieldname}' must be a string"
317
+ end
318
+ return x
319
+ end
320
+
321
+ def validate_description x, fieldname, schema, description=nil
322
+ if !description.nil? && !description.kind_of?(String)
323
+ raise ValueError, "The description for field '#{fieldname}' must be a string"
324
+ end
325
+ return x
326
+ end
327
+
328
+ def validate_format x, fieldname, schema, format=nil
329
+ return x
330
+ end
331
+
332
+ def validate_default x, fieldname, schema, default=nil
333
+ if @interactive && !x.include?(fieldname) && !default.nil?
334
+ unless schema["readonly"]
335
+ x[fieldname] = default
336
+ end
337
+ end
338
+ return x
339
+ end
340
+
341
+ def validate_transient x, fieldname, schema, transient=false
342
+ return x
343
+ end
344
+
345
+ def validate_maxDecimal x, fieldname, schema, maxdecimal=nil
346
+ value = x[fieldname]
347
+ if !maxdecimal.nil? && value
348
+ maxdecstring = value.to_s
349
+ index = maxdecstring.index('.')
350
+ if index && maxdecstring[(index+1)...maxdecstring.size].split(//u).size > maxdecimal
351
+ raise ValueError, "Value #{value} for field '#{fieldname}' must not have more than #{maxdecimal} decimal places"
352
+ end
353
+ end
354
+ return x
355
+ end
356
+
357
+ def validate_hidden x, fieldname, schema, hidden=false
358
+ return x
359
+ end
360
+
361
+ def validate_disallow x, fieldname, schema, disallow=nil
362
+ if !disallow.nil?
363
+ begin
364
+ validate_type(x, fieldname, schema, disallow)
365
+ rescue ValueError
366
+ return x
367
+ end
368
+ raise ValueError, "Value #{x[fieldname]} of type #{disallow} is disallowed for field '#{fieldname}'"
369
+ end
370
+ return x
371
+ end
372
+
373
+ def validate_extends x, fieldname, schema, extends=nil
374
+ return x
375
+ end
376
+
377
+ def convert_type fieldtype
378
+ if TypesList.include?(fieldtype) || fieldtype.kind_of?(Hash)
379
+ return fieldtype
380
+ elsif fieldtype.kind_of? Array
381
+ converted_fields = []
382
+ fieldtype.each do |subfieldtype|
383
+ converted_fields << convert_type(subfieldtype)
384
+ end
385
+ return converted_fields
386
+ elsif !fieldtype
387
+ return nil
388
+ else
389
+ fieldtype = fieldtype.to_s
390
+ if TypesMap.include?(fieldtype)
391
+ return TypesMap[fieldtype]
392
+ else
393
+ raise ValueError, "Field type '#{fieldtype}' is not supported."
394
+ end
395
+ end
396
+ end
397
+
398
+ def __validate fieldname, data, schema
399
+ if schema
400
+ if !schema.kind_of?(Hash)
401
+ raise ValueError, "Schema structure is invalid"
402
+ end
403
+ # copy
404
+ new_schema = Marshal.load(Marshal.dump(schema))
405
+ DefaultSchema.each do |key, val|
406
+ new_schema[key] = val unless new_schema.include?(key)
407
+ end
408
+ new_schema.each do |key ,val|
409
+ validatorname = "validate_"+key
410
+ begin
411
+ __send__(validatorname, data, fieldname, schema, new_schema[key])
412
+ rescue NoMethodError => e
413
+ raise ValueError, "Schema property '#{e.message}' is not supported"
414
+ end
415
+ end
416
+ end
417
+ return data
418
+ end
419
+
420
+ def _validate data, schema
421
+ __validate("_data", {"_data" => data}, schema)
422
+ end
423
+
424
+ class << self
425
+ def validate data, schema, interactive=true
426
+ validator = JSON::Schema.new(interactive)
427
+ validator.validate(data, schema)
428
+ end
429
+ end
430
+ end
431
+ end
432
+