json-schema-pvdgm 2.3.1
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.
- checksums.yaml +7 -0
- data/LICENSE.md +19 -0
- data/README.textile +354 -0
- data/lib/json-schema.rb +25 -0
- data/lib/json-schema/attributes/additionalitems.rb +23 -0
- data/lib/json-schema/attributes/additionalproperties.rb +67 -0
- data/lib/json-schema/attributes/allof.rb +37 -0
- data/lib/json-schema/attributes/anyof.rb +41 -0
- data/lib/json-schema/attributes/dependencies.rb +30 -0
- data/lib/json-schema/attributes/dependencies_v4.rb +20 -0
- data/lib/json-schema/attributes/disallow.rb +11 -0
- data/lib/json-schema/attributes/divisibleby.rb +16 -0
- data/lib/json-schema/attributes/enum.rb +24 -0
- data/lib/json-schema/attributes/extends.rb +49 -0
- data/lib/json-schema/attributes/format.rb +123 -0
- data/lib/json-schema/attributes/items.rb +25 -0
- data/lib/json-schema/attributes/maxdecimal.rb +15 -0
- data/lib/json-schema/attributes/maximum.rb +15 -0
- data/lib/json-schema/attributes/maximum_inclusive.rb +15 -0
- data/lib/json-schema/attributes/maxitems.rb +12 -0
- data/lib/json-schema/attributes/maxlength.rb +14 -0
- data/lib/json-schema/attributes/maxproperties.rb +12 -0
- data/lib/json-schema/attributes/minimum.rb +15 -0
- data/lib/json-schema/attributes/minimum_inclusive.rb +15 -0
- data/lib/json-schema/attributes/minitems.rb +12 -0
- data/lib/json-schema/attributes/minlength.rb +14 -0
- data/lib/json-schema/attributes/minproperties.rb +12 -0
- data/lib/json-schema/attributes/multipleof.rb +16 -0
- data/lib/json-schema/attributes/not.rb +28 -0
- data/lib/json-schema/attributes/oneof.rb +32 -0
- data/lib/json-schema/attributes/pattern.rb +15 -0
- data/lib/json-schema/attributes/patternproperties.rb +23 -0
- data/lib/json-schema/attributes/properties.rb +58 -0
- data/lib/json-schema/attributes/properties_optional.rb +23 -0
- data/lib/json-schema/attributes/properties_v4.rb +57 -0
- data/lib/json-schema/attributes/ref.rb +70 -0
- data/lib/json-schema/attributes/required.rb +23 -0
- data/lib/json-schema/attributes/type.rb +102 -0
- data/lib/json-schema/attributes/type_v4.rb +54 -0
- data/lib/json-schema/attributes/uniqueitems.rb +16 -0
- data/lib/json-schema/model_validator.rb +85 -0
- data/lib/json-schema/schema.rb +73 -0
- data/lib/json-schema/uri/file.rb +36 -0
- data/lib/json-schema/uri/uuid.rb +285 -0
- data/lib/json-schema/util/array_set.rb +14 -0
- data/lib/json-schema/util/hash.rb +8 -0
- data/lib/json-schema/validator.rb +672 -0
- data/lib/json-schema/validators/draft1.rb +32 -0
- data/lib/json-schema/validators/draft2.rb +33 -0
- data/lib/json-schema/validators/draft3.rb +38 -0
- data/lib/json-schema/validators/draft4.rb +45 -0
- data/resources/draft-01.json +155 -0
- data/resources/draft-02.json +166 -0
- data/resources/draft-03.json +174 -0
- data/resources/draft-04.json +150 -0
- data/test/data/all_of_ref_data.json +3 -0
- data/test/data/any_of_ref_data.json +7 -0
- data/test/data/bad_data_1.json +3 -0
- data/test/data/good_data_1.json +3 -0
- data/test/data/one_of_ref_links_data.json +5 -0
- data/test/schemas/all_of_ref_base_schema.json +6 -0
- data/test/schemas/all_of_ref_schema.json +7 -0
- data/test/schemas/any_of_ref_jane_schema.json +4 -0
- data/test/schemas/any_of_ref_jimmy_schema.json +4 -0
- data/test/schemas/any_of_ref_john_schema.json +4 -0
- data/test/schemas/any_of_ref_schema.json +15 -0
- data/test/schemas/extends_and_additionalProperties-1-filename.schema.json +34 -0
- data/test/schemas/extends_and_additionalProperties-1-ref.schema.json +34 -0
- data/test/schemas/extends_and_additionalProperties-2-filename.schema.json +33 -0
- data/test/schemas/extends_and_additionalProperties-2-ref.schema.json +33 -0
- data/test/schemas/good_schema_1.json +10 -0
- data/test/schemas/good_schema_2.json +10 -0
- data/test/schemas/good_schema_extends1.json +10 -0
- data/test/schemas/good_schema_extends2.json +13 -0
- data/test/schemas/inner.schema.json +21 -0
- data/test/schemas/one_of_ref_links_schema.json +16 -0
- data/test/schemas/self_link_schema.json +17 -0
- data/test/schemas/up_link_schema.json +17 -0
- data/test/test_all_of_ref_schema.rb +11 -0
- data/test/test_any_of_ref_schema.rb +11 -0
- data/test/test_bad_schema_ref.rb +33 -0
- data/test/test_extended_schema.rb +68 -0
- data/test/test_extends_and_additionalProperties.rb +50 -0
- data/test/test_files_v3.rb +52 -0
- data/test/test_fragment_resolution.rb +31 -0
- data/test/test_full_validation.rb +209 -0
- data/test/test_jsonschema_draft1.rb +701 -0
- data/test/test_jsonschema_draft2.rb +773 -0
- data/test/test_jsonschema_draft3.rb +1236 -0
- data/test/test_jsonschema_draft4.rb +1356 -0
- data/test/test_model_validator.rb +52 -0
- data/test/test_one_of.rb +42 -0
- data/test/test_ruby_schema.rb +38 -0
- data/test/test_schema_type_attribute.rb +21 -0
- data/test/test_schema_validation.rb +85 -0
- metadata +180 -0
@@ -0,0 +1,14 @@
|
|
1
|
+
# This is a hack that I don't want to ever use anywhere else or repeat EVER, but we need enums to be
|
2
|
+
# an Array to pass schema validation. But we also want fast lookup! And we can't use sets because of
|
3
|
+
# backport support... so...
|
4
|
+
|
5
|
+
class ArraySet < Array
|
6
|
+
def include?(obj)
|
7
|
+
# On first invocation create a HASH (yeah, yeah) to act as our set given the array values
|
8
|
+
if !defined? @array_values
|
9
|
+
@array_values = {}
|
10
|
+
self.each {|x| @array_values[x] = 1}
|
11
|
+
end
|
12
|
+
@array_values.has_key? obj
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,672 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'open-uri'
|
3
|
+
require 'pathname'
|
4
|
+
require 'bigdecimal'
|
5
|
+
require 'digest/sha1'
|
6
|
+
require 'date'
|
7
|
+
require 'thread'
|
8
|
+
require 'yaml'
|
9
|
+
|
10
|
+
module JSON
|
11
|
+
|
12
|
+
class Schema
|
13
|
+
class ValidationError < StandardError
|
14
|
+
attr_accessor :fragments, :schema, :failed_attribute, :sub_errors, :error_details
|
15
|
+
|
16
|
+
def initialize(message, fragments, failed_attribute, schema, error_details={})
|
17
|
+
@fragments = fragments.clone
|
18
|
+
@schema = schema
|
19
|
+
@sub_errors = []
|
20
|
+
@failed_attribute = failed_attribute
|
21
|
+
@error_details = error_details
|
22
|
+
message = "#{message} in schema #{schema.uri}"
|
23
|
+
super(message)
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_string
|
27
|
+
if @sub_errors.empty?
|
28
|
+
message
|
29
|
+
else
|
30
|
+
full_message = message + "\n The schema specific errors were:\n"
|
31
|
+
@sub_errors.each{|e| full_message = full_message + " - " + e.to_string + "\n"}
|
32
|
+
full_message
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_hash
|
37
|
+
base = {:schema => @schema.uri, :fragment => ::JSON::Schema::Attribute.build_fragment(fragments), :message => message, :failed_attribute => @failed_attribute.to_s.split(":").last.split("Attribute").first, :error_details => @error_details}
|
38
|
+
if !@sub_errors.empty?
|
39
|
+
base[:errors] = @sub_errors.map{|e| e.to_hash}
|
40
|
+
end
|
41
|
+
base
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class SchemaError < StandardError
|
46
|
+
end
|
47
|
+
|
48
|
+
class JsonParseError < StandardError
|
49
|
+
end
|
50
|
+
|
51
|
+
class Attribute
|
52
|
+
def self.validate(current_schema, data, fragments, processor, validator, options = {})
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.build_fragment(fragments)
|
56
|
+
"#/#{fragments.join('/')}"
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.last_fragment_as_symbol(fragments)
|
60
|
+
return nil if fragments.empty?
|
61
|
+
fragments.first.to_sym
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.validation_error(processor, message, fragments, current_schema, failed_attribute, record_errors, error_details={})
|
65
|
+
error = ValidationError.new(message, fragments, failed_attribute, current_schema, error_details)
|
66
|
+
if record_errors
|
67
|
+
processor.validation_error(error)
|
68
|
+
else
|
69
|
+
raise error
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.validation_errors(validator)
|
74
|
+
validator.validation_errors
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class Validator
|
79
|
+
attr_accessor :attributes, :uri
|
80
|
+
|
81
|
+
def initialize()
|
82
|
+
@attributes = {}
|
83
|
+
@uri = nil
|
84
|
+
end
|
85
|
+
|
86
|
+
def extend_schema_definition(schema_uri)
|
87
|
+
u = URI.parse(schema_uri)
|
88
|
+
validator = JSON::Validator.validators["#{u.scheme}://#{u.host}#{u.path}"]
|
89
|
+
if validator.nil?
|
90
|
+
raise SchemaError.new("Schema not found: #{u.scheme}://#{u.host}#{u.path}")
|
91
|
+
end
|
92
|
+
@attributes.merge!(validator.attributes)
|
93
|
+
end
|
94
|
+
|
95
|
+
def to_s
|
96
|
+
"#{@uri.scheme}://#{uri.host}#{uri.path}"
|
97
|
+
end
|
98
|
+
|
99
|
+
def validate(current_schema, data, fragments, processor, options = {})
|
100
|
+
current_schema.schema.each do |attr_name,attribute|
|
101
|
+
if @attributes.has_key?(attr_name.to_s)
|
102
|
+
@attributes[attr_name.to_s].validate(current_schema, data, fragments, processor, self, options)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
data
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
class Validator
|
112
|
+
|
113
|
+
@@schemas = {}
|
114
|
+
@@cache_schemas = false
|
115
|
+
@@default_opts = {
|
116
|
+
:list => false,
|
117
|
+
:version => nil,
|
118
|
+
:validate_schema => false,
|
119
|
+
:record_errors => false,
|
120
|
+
:errors_as_objects => false,
|
121
|
+
:insert_defaults => false,
|
122
|
+
:clear_cache => true,
|
123
|
+
:strict => false
|
124
|
+
}
|
125
|
+
@@validators = {}
|
126
|
+
@@default_validator = nil
|
127
|
+
@@available_json_backends = []
|
128
|
+
@@json_backend = nil
|
129
|
+
@@serializer = nil
|
130
|
+
@@mutex = Mutex.new
|
131
|
+
|
132
|
+
def self.version_string_for(version)
|
133
|
+
# I'm not a fan of this, but it's quick and dirty to get it working for now
|
134
|
+
return "draft-04" unless version
|
135
|
+
case version.to_s
|
136
|
+
when "draft4", "http://json-schema.org/draft-04/schema#"
|
137
|
+
"draft-04"
|
138
|
+
when "draft3", "http://json-schema.org/draft-03/schema#"
|
139
|
+
"draft-03"
|
140
|
+
when "draft2"
|
141
|
+
"draft-02"
|
142
|
+
when "draft1"
|
143
|
+
"draft-01"
|
144
|
+
else
|
145
|
+
raise JSON::Schema::SchemaError.new("The requested JSON schema version is not supported")
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def self.metaschema_for(version_string)
|
150
|
+
File.join(Pathname.new(File.dirname(__FILE__)).parent.parent, "resources", "#{version_string}.json").to_s
|
151
|
+
end
|
152
|
+
|
153
|
+
def initialize(schema_data, data, opts={})
|
154
|
+
@options = @@default_opts.clone.merge(opts)
|
155
|
+
@errors = []
|
156
|
+
|
157
|
+
# I'm not a fan of this, but it's quick and dirty to get it working for now
|
158
|
+
version_string = "draft-04"
|
159
|
+
if @options[:version]
|
160
|
+
version_string = @options[:version] = self.class.version_string_for(@options[:version])
|
161
|
+
u = URI.parse("http://json-schema.org/#{@options[:version]}/schema#")
|
162
|
+
validator = JSON::Validator.validators["#{u.scheme}://#{u.host}#{u.path}"]
|
163
|
+
@options[:version] = validator
|
164
|
+
end
|
165
|
+
|
166
|
+
@validation_options = @options[:record_errors] ? {:record_errors => true} : {}
|
167
|
+
@validation_options[:insert_defaults] = true if @options[:insert_defaults]
|
168
|
+
@validation_options[:strict] = true if @options[:strict] == true
|
169
|
+
|
170
|
+
@@mutex.synchronize { @base_schema = initialize_schema(schema_data) }
|
171
|
+
@data = initialize_data(data)
|
172
|
+
@@mutex.synchronize { build_schemas(@base_schema) }
|
173
|
+
|
174
|
+
# validate the schema, if requested
|
175
|
+
if @options[:validate_schema]
|
176
|
+
begin
|
177
|
+
if @base_schema.schema["$schema"]
|
178
|
+
version_string = @options[:version] = self.class.version_string_for(@base_schema.schema["$schema"])
|
179
|
+
end
|
180
|
+
# Don't clear the cache during metaschema validation!
|
181
|
+
meta_validator = JSON::Validator.new(self.class.metaschema_for(version_string), @base_schema.schema, {:clear_cache => false})
|
182
|
+
meta_validator.validate
|
183
|
+
rescue JSON::Schema::ValidationError, JSON::Schema::SchemaError
|
184
|
+
raise $!
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
# If the :fragment option is set, try and validate against the fragment
|
189
|
+
if opts[:fragment]
|
190
|
+
@base_schema = schema_from_fragment(@base_schema, opts[:fragment])
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def schema_from_fragment(base_schema, fragment)
|
195
|
+
fragments = fragment.split("/")
|
196
|
+
|
197
|
+
# ensure the first element was a hash, per the fragment spec
|
198
|
+
if fragments.shift != "#"
|
199
|
+
raise JSON::Schema::SchemaError.new("Invalid fragment syntax in :fragment option")
|
200
|
+
end
|
201
|
+
|
202
|
+
fragments.each do |f|
|
203
|
+
if base_schema.is_a?(JSON::Schema) #test if fragment is a JSON:Schema instance
|
204
|
+
if !base_schema.schema.has_key?(f)
|
205
|
+
raise JSON::Schema::SchemaError.new("Invalid fragment resolution for :fragment option")
|
206
|
+
end
|
207
|
+
base_schema = base_schema.schema[f]
|
208
|
+
elsif base_schema.is_a?(Hash)
|
209
|
+
if !base_schema.has_key?(f)
|
210
|
+
raise JSON::Schema::SchemaError.new("Invalid fragment resolution for :fragment option")
|
211
|
+
end
|
212
|
+
base_schema = initialize_schema(base_schema[f]) #need to return a Schema instance for validation to work
|
213
|
+
elsif base_schema.is_a?(Array)
|
214
|
+
if base_schema[f.to_i].nil?
|
215
|
+
raise JSON::Schema::SchemaError.new("Invalid fragment resolution for :fragment option")
|
216
|
+
end
|
217
|
+
base_schema = initialize_schema(base_schema[f.to_i])
|
218
|
+
else
|
219
|
+
raise JSON::Schema::SchemaError.new("Invalid schema encountered when resolving :fragment option")
|
220
|
+
end
|
221
|
+
end
|
222
|
+
if @options[:list] #check if the schema is validating a list
|
223
|
+
base_schema.schema = schema_to_list(base_schema.schema)
|
224
|
+
end
|
225
|
+
base_schema
|
226
|
+
end
|
227
|
+
|
228
|
+
# Run a simple true/false validation of data against a schema
|
229
|
+
def validate()
|
230
|
+
begin
|
231
|
+
@base_schema.validate(@data,[],self,@validation_options)
|
232
|
+
if @validation_options[:clear_cache] == true
|
233
|
+
Validator.clear_cache
|
234
|
+
end
|
235
|
+
if @options[:errors_as_objects]
|
236
|
+
return @errors.map{|e| e.to_hash}
|
237
|
+
else
|
238
|
+
return @errors.map{|e| e.to_string}
|
239
|
+
end
|
240
|
+
rescue JSON::Schema::ValidationError
|
241
|
+
if @validation_options[:clear_cache] == true
|
242
|
+
Validator.clear_cache
|
243
|
+
end
|
244
|
+
raise $!
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
|
249
|
+
def load_ref_schema(parent_schema,ref)
|
250
|
+
uri = URI.parse(ref)
|
251
|
+
if uri.relative?
|
252
|
+
uri = parent_schema.uri.clone
|
253
|
+
|
254
|
+
# Check for absolute path
|
255
|
+
path = ref.split("#")[0]
|
256
|
+
|
257
|
+
# This is a self reference and thus the schema does not need to be re-loaded
|
258
|
+
if path.nil? || path == ''
|
259
|
+
return
|
260
|
+
end
|
261
|
+
|
262
|
+
if path && path[0,1] == '/'
|
263
|
+
uri.path = Pathname.new(path).cleanpath.to_s
|
264
|
+
else
|
265
|
+
uri = parent_schema.uri.merge(path)
|
266
|
+
end
|
267
|
+
uri.fragment = ''
|
268
|
+
end
|
269
|
+
|
270
|
+
if Validator.schemas[uri.to_s].nil?
|
271
|
+
schema = JSON::Schema.new(JSON::Validator.parse(open(uri.to_s).read), uri, @options[:version])
|
272
|
+
Validator.add_schema(schema)
|
273
|
+
build_schemas(schema)
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
|
278
|
+
# Build all schemas with IDs, mapping out the namespace
|
279
|
+
def build_schemas(parent_schema)
|
280
|
+
# Build ref schemas if they exist
|
281
|
+
if parent_schema.schema["$ref"]
|
282
|
+
load_ref_schema(parent_schema, parent_schema.schema["$ref"])
|
283
|
+
end
|
284
|
+
if parent_schema.schema["extends"]
|
285
|
+
if parent_schema.schema["extends"].is_a?(String)
|
286
|
+
load_ref_schema(parent_schema, parent_schema.schema["extends"])
|
287
|
+
elsif parent_schema.schema["extends"].is_a?(Array)
|
288
|
+
parent_schema.schema["extends"].each do |type|
|
289
|
+
handle_schema(parent_schema, type)
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
# handle validations that always contain schemas
|
295
|
+
["allOf", "anyOf", "oneOf", "not"].each do |key|
|
296
|
+
if parent_schema.schema.has_key?(key)
|
297
|
+
validations = parent_schema.schema[key]
|
298
|
+
validations = [validations] unless validations.is_a?(Array)
|
299
|
+
validations.each {|v| handle_schema(parent_schema, v) }
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
# Check for schemas in union types
|
304
|
+
["type", "disallow"].each do |key|
|
305
|
+
if parent_schema.schema[key] && parent_schema.schema[key].is_a?(Array)
|
306
|
+
parent_schema.schema[key].each_with_index do |type,i|
|
307
|
+
if type.is_a?(Hash)
|
308
|
+
handle_schema(parent_schema, type)
|
309
|
+
end
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
# "definitions" are schemas in V4
|
315
|
+
if parent_schema.schema["definitions"]
|
316
|
+
parent_schema.schema["definitions"].each do |k,v|
|
317
|
+
handle_schema(parent_schema, v)
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
# All properties are schemas
|
322
|
+
if parent_schema.schema["properties"]
|
323
|
+
parent_schema.schema["properties"].each do |k,v|
|
324
|
+
handle_schema(parent_schema, v)
|
325
|
+
end
|
326
|
+
end
|
327
|
+
if parent_schema.schema["patternProperties"]
|
328
|
+
parent_schema.schema["patternProperties"].each do |k,v|
|
329
|
+
handle_schema(parent_schema, v)
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
# Items are always schemas
|
334
|
+
if parent_schema.schema["items"]
|
335
|
+
items = parent_schema.schema["items"].clone
|
336
|
+
single = false
|
337
|
+
if !items.is_a?(Array)
|
338
|
+
items = [items]
|
339
|
+
single = true
|
340
|
+
end
|
341
|
+
items.each_with_index do |item,i|
|
342
|
+
handle_schema(parent_schema, item)
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
# Convert enum to a ArraySet
|
347
|
+
if parent_schema.schema["enum"] && parent_schema.schema["enum"].is_a?(Array)
|
348
|
+
parent_schema.schema["enum"] = ArraySet.new(parent_schema.schema["enum"])
|
349
|
+
end
|
350
|
+
|
351
|
+
# Each of these might be schemas
|
352
|
+
["additionalProperties", "additionalItems", "dependencies", "extends"].each do |key|
|
353
|
+
if parent_schema.schema[key].is_a?(Hash)
|
354
|
+
handle_schema(parent_schema, parent_schema.schema[key])
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
end
|
359
|
+
|
360
|
+
# Either load a reference schema or create a new schema
|
361
|
+
def handle_schema(parent_schema, obj)
|
362
|
+
if obj.is_a?(Hash)
|
363
|
+
schema_uri = parent_schema.uri.clone
|
364
|
+
schema = JSON::Schema.new(obj,schema_uri,parent_schema.validator)
|
365
|
+
if obj['id']
|
366
|
+
Validator.add_schema(schema)
|
367
|
+
end
|
368
|
+
build_schemas(schema)
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
def validation_error(error)
|
373
|
+
@errors.push(error)
|
374
|
+
end
|
375
|
+
|
376
|
+
def validation_errors
|
377
|
+
@errors
|
378
|
+
end
|
379
|
+
|
380
|
+
|
381
|
+
class << self
|
382
|
+
def validate(schema, data,opts={})
|
383
|
+
begin
|
384
|
+
validator = JSON::Validator.new(schema, data, opts)
|
385
|
+
validator.validate
|
386
|
+
return true
|
387
|
+
rescue JSON::Schema::ValidationError, JSON::Schema::SchemaError
|
388
|
+
return false
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
def validate_json(schema, data, opts={})
|
393
|
+
validate(schema, data, opts.merge(:json => true))
|
394
|
+
end
|
395
|
+
|
396
|
+
def validate_uri(schema, data, opts={})
|
397
|
+
validate(schema, data, opts.merge(:uri => true))
|
398
|
+
end
|
399
|
+
|
400
|
+
def validate!(schema, data,opts={})
|
401
|
+
validator = JSON::Validator.new(schema, data, opts)
|
402
|
+
validator.validate
|
403
|
+
return true
|
404
|
+
end
|
405
|
+
alias_method 'validate2', 'validate!'
|
406
|
+
|
407
|
+
def validate_json!(schema, data, opts={})
|
408
|
+
validate!(schema, data, opts.merge(:json => true))
|
409
|
+
end
|
410
|
+
|
411
|
+
def validate_uri!(schema, data, opts={})
|
412
|
+
validate!(schema, data, opts.merge(:uri => true))
|
413
|
+
end
|
414
|
+
|
415
|
+
def fully_validate(schema, data, opts={})
|
416
|
+
opts[:record_errors] = true
|
417
|
+
validator = JSON::Validator.new(schema, data, opts)
|
418
|
+
validator.validate
|
419
|
+
end
|
420
|
+
|
421
|
+
def fully_validate_schema(schema, opts={})
|
422
|
+
data = schema
|
423
|
+
schema = metaschema_for(version_string_for(opts[:version]))
|
424
|
+
fully_validate(schema, data, opts)
|
425
|
+
end
|
426
|
+
|
427
|
+
def fully_validate_json(schema, data, opts={})
|
428
|
+
fully_validate(schema, data, opts.merge(:json => true))
|
429
|
+
end
|
430
|
+
|
431
|
+
def fully_validate_uri(schema, data, opts={})
|
432
|
+
fully_validate(schema, data, opts.merge(:uri => true))
|
433
|
+
end
|
434
|
+
|
435
|
+
def clear_cache
|
436
|
+
@@schemas = {} if @@cache_schemas == false
|
437
|
+
end
|
438
|
+
|
439
|
+
def schemas
|
440
|
+
@@schemas
|
441
|
+
end
|
442
|
+
|
443
|
+
def add_schema(schema)
|
444
|
+
@@schemas[schema.uri.to_s] = schema if @@schemas[schema.uri.to_s].nil?
|
445
|
+
end
|
446
|
+
|
447
|
+
def cache_schemas=(val)
|
448
|
+
warn "[DEPRECATION NOTICE] Schema caching is now a validation option. Schemas will still be cached if this is set to true, but this method will be removed in version >= 3. Please use the :clear_cache validation option instead."
|
449
|
+
@@cache_schemas = val == true ? true : false
|
450
|
+
end
|
451
|
+
|
452
|
+
def validators
|
453
|
+
@@validators
|
454
|
+
end
|
455
|
+
|
456
|
+
def default_validator
|
457
|
+
@@default_validator
|
458
|
+
end
|
459
|
+
|
460
|
+
def register_validator(v)
|
461
|
+
@@validators[v.to_s] = v
|
462
|
+
end
|
463
|
+
|
464
|
+
def register_default_validator(v)
|
465
|
+
@@default_validator = v
|
466
|
+
end
|
467
|
+
|
468
|
+
def json_backend
|
469
|
+
if defined?(MultiJson)
|
470
|
+
MultiJson.respond_to?(:adapter) ? MultiJson.adapter : MultiJson.engine
|
471
|
+
else
|
472
|
+
@@json_backend
|
473
|
+
end
|
474
|
+
end
|
475
|
+
|
476
|
+
def json_backend=(backend)
|
477
|
+
if defined?(MultiJson)
|
478
|
+
backend = backend == 'json' ? 'json_gem' : backend
|
479
|
+
MultiJson.respond_to?(:use) ? MultiJson.use(backend) : MultiJson.engine = backend
|
480
|
+
else
|
481
|
+
backend = backend.to_s
|
482
|
+
if @@available_json_backends.include?(backend)
|
483
|
+
@@json_backend = backend
|
484
|
+
else
|
485
|
+
raise JSON::Schema::JsonParseError.new("The JSON backend '#{backend}' could not be found.")
|
486
|
+
end
|
487
|
+
end
|
488
|
+
end
|
489
|
+
|
490
|
+
def parse(s)
|
491
|
+
if defined?(MultiJson)
|
492
|
+
MultiJson.respond_to?(:adapter) ? MultiJson.load(s) : MultiJson.decode(s)
|
493
|
+
else
|
494
|
+
case @@json_backend.to_s
|
495
|
+
when 'json'
|
496
|
+
JSON.parse(s)
|
497
|
+
when 'yajl'
|
498
|
+
json = StringIO.new(s)
|
499
|
+
parser = Yajl::Parser.new
|
500
|
+
parser.parse(json)
|
501
|
+
else
|
502
|
+
raise JSON::Schema::JsonParseError.new("No supported JSON parsers found. The following parsers are suported:\n * yajl-ruby\n * json")
|
503
|
+
end
|
504
|
+
end
|
505
|
+
end
|
506
|
+
|
507
|
+
if !defined?(MultiJson)
|
508
|
+
if begin
|
509
|
+
Gem::Specification::find_by_name('json')
|
510
|
+
rescue Gem::LoadError
|
511
|
+
false
|
512
|
+
rescue
|
513
|
+
Gem.available?('json')
|
514
|
+
end
|
515
|
+
require 'json'
|
516
|
+
@@available_json_backends << 'json'
|
517
|
+
@@json_backend = 'json'
|
518
|
+
end
|
519
|
+
|
520
|
+
# Try force-loading json for rubies > 1.9.2
|
521
|
+
begin
|
522
|
+
require 'json'
|
523
|
+
@@available_json_backends << 'json'
|
524
|
+
@@json_backend = 'json'
|
525
|
+
rescue LoadError
|
526
|
+
end
|
527
|
+
|
528
|
+
if begin
|
529
|
+
Gem::Specification::find_by_name('yajl-ruby')
|
530
|
+
rescue Gem::LoadError
|
531
|
+
false
|
532
|
+
rescue
|
533
|
+
Gem.available?('yajl-ruby')
|
534
|
+
end
|
535
|
+
require 'yajl'
|
536
|
+
@@available_json_backends << 'yajl'
|
537
|
+
@@json_backend = 'yajl'
|
538
|
+
end
|
539
|
+
|
540
|
+
if @@json_backend == 'yajl'
|
541
|
+
@@serializer = lambda{|o| Yajl::Encoder.encode(o) }
|
542
|
+
else
|
543
|
+
@@serializer = lambda{|o|
|
544
|
+
YAML.dump(o)
|
545
|
+
}
|
546
|
+
end
|
547
|
+
end
|
548
|
+
end
|
549
|
+
|
550
|
+
private
|
551
|
+
|
552
|
+
if begin
|
553
|
+
Gem::Specification::find_by_name('uuidtools')
|
554
|
+
rescue Gem::LoadError
|
555
|
+
false
|
556
|
+
rescue
|
557
|
+
Gem.available?('uuidtools')
|
558
|
+
end
|
559
|
+
require 'uuidtools'
|
560
|
+
@@fake_uri_generator = lambda{|s| UUIDTools::UUID.sha1_create(UUIDTools::UUID_URL_NAMESPACE, s).to_s }
|
561
|
+
else
|
562
|
+
require 'json-schema/uri/uuid'
|
563
|
+
@@fake_uri_generator = lambda{|s| JSON::Util::UUID.create_v5(s,JSON::Util::UUID::Nil).to_s }
|
564
|
+
end
|
565
|
+
|
566
|
+
def serialize schema
|
567
|
+
if defined?(MultiJson)
|
568
|
+
MultiJson.respond_to?(:dump) ? MultiJson.dump(schema) : MultiJson.encode(schema)
|
569
|
+
else
|
570
|
+
@@serializer.call(schema)
|
571
|
+
end
|
572
|
+
end
|
573
|
+
|
574
|
+
def fake_uri schema
|
575
|
+
@@fake_uri_generator.call(schema)
|
576
|
+
end
|
577
|
+
|
578
|
+
def schema_to_list(schema)
|
579
|
+
new_schema = {"type" => "array", "items" => schema}
|
580
|
+
if !schema["$schema"].nil?
|
581
|
+
new_schema["$schema"] = schema["$schema"]
|
582
|
+
end
|
583
|
+
|
584
|
+
new_schema
|
585
|
+
end
|
586
|
+
|
587
|
+
def initialize_schema(schema)
|
588
|
+
if schema.is_a?(String)
|
589
|
+
begin
|
590
|
+
# Build a fake URI for this
|
591
|
+
schema_uri = URI.parse(fake_uri(schema))
|
592
|
+
schema = JSON::Validator.parse(schema)
|
593
|
+
if @options[:list] && @options[:fragment].nil?
|
594
|
+
schema = schema_to_list(schema)
|
595
|
+
end
|
596
|
+
schema = JSON::Schema.new(schema,schema_uri,@options[:version])
|
597
|
+
Validator.add_schema(schema)
|
598
|
+
rescue
|
599
|
+
# Build a uri for it
|
600
|
+
schema_uri = URI.parse(schema)
|
601
|
+
if schema_uri.relative?
|
602
|
+
# Check for absolute path
|
603
|
+
if schema[0,1] == '/'
|
604
|
+
schema_uri = URI.parse("file://#{schema}")
|
605
|
+
else
|
606
|
+
schema_uri = URI.parse("file://#{Dir.pwd}/#{schema}")
|
607
|
+
end
|
608
|
+
end
|
609
|
+
if Validator.schemas[schema_uri.to_s].nil?
|
610
|
+
schema = JSON::Validator.parse(open(schema_uri.to_s).read)
|
611
|
+
if @options[:list] && @options[:fragment].nil?
|
612
|
+
schema = schema_to_list(schema)
|
613
|
+
end
|
614
|
+
schema = JSON::Schema.new(schema,schema_uri,@options[:version])
|
615
|
+
Validator.add_schema(schema)
|
616
|
+
else
|
617
|
+
schema = Validator.schemas[schema_uri.to_s]
|
618
|
+
end
|
619
|
+
end
|
620
|
+
elsif schema.is_a?(Hash)
|
621
|
+
if @options[:list] && @options[:fragment].nil?
|
622
|
+
schema = schema_to_list(schema)
|
623
|
+
end
|
624
|
+
schema_uri = URI.parse(fake_uri(serialize(schema)))
|
625
|
+
schema = JSON::Schema.new(schema,schema_uri,@options[:version])
|
626
|
+
Validator.add_schema(schema)
|
627
|
+
else
|
628
|
+
raise "Invalid schema - must be either a string or a hash"
|
629
|
+
end
|
630
|
+
|
631
|
+
schema
|
632
|
+
end
|
633
|
+
|
634
|
+
|
635
|
+
def initialize_data(data)
|
636
|
+
if @options[:json]
|
637
|
+
data = JSON::Validator.parse(data)
|
638
|
+
elsif @options[:uri]
|
639
|
+
json_uri = URI.parse(data)
|
640
|
+
if json_uri.relative?
|
641
|
+
if data[0,1] == '/'
|
642
|
+
json_uri = URI.parse("file://#{data}")
|
643
|
+
else
|
644
|
+
json_uri = URI.parse("file://#{Dir.pwd}/#{data}")
|
645
|
+
end
|
646
|
+
end
|
647
|
+
data = JSON::Validator.parse(open(json_uri.to_s).read)
|
648
|
+
elsif data.is_a?(String)
|
649
|
+
begin
|
650
|
+
data = JSON::Validator.parse(data)
|
651
|
+
rescue
|
652
|
+
begin
|
653
|
+
json_uri = URI.parse(data)
|
654
|
+
if json_uri.relative?
|
655
|
+
if data[0,1] == '/'
|
656
|
+
json_uri = URI.parse("file://#{data}")
|
657
|
+
else
|
658
|
+
json_uri = URI.parse("file://#{Dir.pwd}/#{data}")
|
659
|
+
end
|
660
|
+
end
|
661
|
+
data = JSON::Validator.parse(open(json_uri.to_s).read)
|
662
|
+
rescue
|
663
|
+
# Silently discard the error - the data will not change
|
664
|
+
end
|
665
|
+
end
|
666
|
+
end
|
667
|
+
JSON::Schema.add_indifferent_access(data)
|
668
|
+
data
|
669
|
+
end
|
670
|
+
|
671
|
+
end
|
672
|
+
end
|