jsonschema 1.0.0

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