json-schema-openc-fork 0.0.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 +15 -0
- data/LICENSE.md +19 -0
- data/README.textile +452 -0
- data/lib/json-schema.rb +19 -0
- data/lib/json-schema/attribute.rb +43 -0
- data/lib/json-schema/attributes/additionalitems.rb +28 -0
- data/lib/json-schema/attributes/additionalproperties.rb +58 -0
- data/lib/json-schema/attributes/allof.rb +39 -0
- data/lib/json-schema/attributes/anyof.rb +47 -0
- data/lib/json-schema/attributes/dependencies.rb +44 -0
- data/lib/json-schema/attributes/disallow.rb +12 -0
- data/lib/json-schema/attributes/divisibleby.rb +22 -0
- data/lib/json-schema/attributes/enum.rb +24 -0
- data/lib/json-schema/attributes/extends.rb +50 -0
- data/lib/json-schema/attributes/format.rb +14 -0
- data/lib/json-schema/attributes/formats/custom.rb +21 -0
- data/lib/json-schema/attributes/formats/date.rb +24 -0
- data/lib/json-schema/attributes/formats/date_time.rb +36 -0
- data/lib/json-schema/attributes/formats/date_time_v4.rb +15 -0
- data/lib/json-schema/attributes/formats/ip.rb +41 -0
- data/lib/json-schema/attributes/formats/time.rb +22 -0
- data/lib/json-schema/attributes/formats/uri.rb +20 -0
- data/lib/json-schema/attributes/items.rb +26 -0
- data/lib/json-schema/attributes/limit.rb +179 -0
- data/lib/json-schema/attributes/maxdecimal.rb +18 -0
- data/lib/json-schema/attributes/multipleof.rb +11 -0
- data/lib/json-schema/attributes/not.rb +30 -0
- data/lib/json-schema/attributes/oneof.rb +56 -0
- data/lib/json-schema/attributes/pattern.rb +18 -0
- data/lib/json-schema/attributes/patternproperties.rb +22 -0
- data/lib/json-schema/attributes/properties.rb +74 -0
- data/lib/json-schema/attributes/properties_optional.rb +26 -0
- data/lib/json-schema/attributes/ref.rb +74 -0
- data/lib/json-schema/attributes/required.rb +28 -0
- data/lib/json-schema/attributes/type.rb +83 -0
- data/lib/json-schema/attributes/type_v4.rb +29 -0
- data/lib/json-schema/attributes/uniqueitems.rb +16 -0
- data/lib/json-schema/errors/custom_format_error.rb +6 -0
- data/lib/json-schema/errors/json_parse_error.rb +6 -0
- data/lib/json-schema/errors/schema_error.rb +6 -0
- data/lib/json-schema/errors/validation_error.rb +46 -0
- data/lib/json-schema/schema.rb +63 -0
- data/lib/json-schema/schema/reader.rb +113 -0
- data/lib/json-schema/schema/validator.rb +36 -0
- data/lib/json-schema/util/array_set.rb +14 -0
- data/lib/json-schema/util/uri.rb +16 -0
- data/lib/json-schema/util/uuid.rb +285 -0
- data/lib/json-schema/validator.rb +592 -0
- data/lib/json-schema/validators/draft1.rb +45 -0
- data/lib/json-schema/validators/draft2.rb +46 -0
- data/lib/json-schema/validators/draft3.rb +50 -0
- data/lib/json-schema/validators/draft4.rb +56 -0
- data/lib/json-schema/validators/hyper-draft4.rb +14 -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/address_microformat.json +18 -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/definition_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/ref john with spaces schema.json +11 -0
- data/test/schemas/relative_definition_schema.json +8 -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 +35 -0
- data/test/test_any_of_ref_schema.rb +35 -0
- data/test/test_bad_schema_ref.rb +39 -0
- data/test/test_common_test_suite.rb +66 -0
- data/test/test_custom_format.rb +116 -0
- data/test/test_definition.rb +15 -0
- data/test/test_extended_schema.rb +62 -0
- data/test/test_extends_and_additionalProperties.rb +52 -0
- data/test/test_files_v3.rb +43 -0
- data/test/test_fragment_resolution.rb +30 -0
- data/test/test_fragment_validation_with_ref.rb +34 -0
- data/test/test_full_validation.rb +208 -0
- data/test/test_helper.rb +47 -0
- data/test/test_initialize_data.rb +118 -0
- data/test/test_jsonschema_draft1.rb +171 -0
- data/test/test_jsonschema_draft2.rb +142 -0
- data/test/test_jsonschema_draft3.rb +502 -0
- data/test/test_jsonschema_draft4.rb +704 -0
- data/test/test_list_option.rb +21 -0
- data/test/test_merge_missing_values.rb +45 -0
- data/test/test_minitems.rb +16 -0
- data/test/test_one_of.rb +85 -0
- data/test/test_ruby_schema.rb +59 -0
- data/test/test_schema_loader.rb +74 -0
- data/test/test_schema_type_attribute.rb +20 -0
- data/test/test_schema_validation.rb +185 -0
- data/test/test_stringify.rb +48 -0
- data/test/test_uri_related.rb +67 -0
- data/test/test_validator.rb +53 -0
- metadata +284 -0
@@ -0,0 +1,592 @@
|
|
1
|
+
require 'addressable/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
|
+
require 'json-schema/schema/reader'
|
11
|
+
require 'json-schema/errors/schema_error'
|
12
|
+
require 'json-schema/errors/json_parse_error'
|
13
|
+
|
14
|
+
module JSON
|
15
|
+
|
16
|
+
class Validator
|
17
|
+
|
18
|
+
@@schemas = {}
|
19
|
+
@@cache_schemas = false
|
20
|
+
@@default_opts = {
|
21
|
+
:list => false,
|
22
|
+
:version => nil,
|
23
|
+
:validate_schema => false,
|
24
|
+
:record_errors => false,
|
25
|
+
:errors_as_objects => false,
|
26
|
+
:insert_defaults => false,
|
27
|
+
:clear_cache => true,
|
28
|
+
:strict => false,
|
29
|
+
:parse_data => true
|
30
|
+
}
|
31
|
+
@@validators = {}
|
32
|
+
@@default_validator = nil
|
33
|
+
@@available_json_backends = []
|
34
|
+
@@json_backend = nil
|
35
|
+
@@serializer = nil
|
36
|
+
@@mutex = Mutex.new
|
37
|
+
|
38
|
+
def initialize(schema_data, data, opts={})
|
39
|
+
@options = @@default_opts.clone.merge(opts)
|
40
|
+
@errors = []
|
41
|
+
|
42
|
+
validator = JSON::Validator.validator_for_name(@options[:version])
|
43
|
+
@options[:version] = validator
|
44
|
+
@options[:schema_reader] ||= JSON::Validator.schema_reader
|
45
|
+
|
46
|
+
@validation_options = @options[:record_errors] ? {:record_errors => true} : {}
|
47
|
+
@validation_options[:insert_defaults] = true if @options[:insert_defaults]
|
48
|
+
@validation_options[:strict] = true if @options[:strict] == true
|
49
|
+
|
50
|
+
@@mutex.synchronize { @base_schema = initialize_schema(schema_data) }
|
51
|
+
@original_data = data
|
52
|
+
@data = initialize_data(data)
|
53
|
+
@@mutex.synchronize { build_schemas(@base_schema) }
|
54
|
+
|
55
|
+
# validate the schema, if requested
|
56
|
+
if @options[:validate_schema]
|
57
|
+
begin
|
58
|
+
if @base_schema.schema["$schema"]
|
59
|
+
base_validator = JSON::Validator.validator_for_name(@base_schema.schema["$schema"])
|
60
|
+
end
|
61
|
+
metaschema = base_validator ? base_validator.metaschema : validator.metaschema
|
62
|
+
# Don't clear the cache during metaschema validation!
|
63
|
+
meta_validator = JSON::Validator.new(metaschema, @base_schema.schema, {:clear_cache => false})
|
64
|
+
meta_validator.validate
|
65
|
+
rescue JSON::Schema::ValidationError, JSON::Schema::SchemaError
|
66
|
+
raise $!
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# If the :fragment option is set, try and validate against the fragment
|
71
|
+
if opts[:fragment]
|
72
|
+
@base_schema = schema_from_fragment(@base_schema, opts[:fragment])
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def schema_from_fragment(base_schema, fragment)
|
77
|
+
schema_uri = base_schema.uri
|
78
|
+
fragments = fragment.split("/")
|
79
|
+
|
80
|
+
# ensure the first element was a hash, per the fragment spec
|
81
|
+
if fragments.shift != "#"
|
82
|
+
raise JSON::Schema::SchemaError.new("Invalid fragment syntax in :fragment option")
|
83
|
+
end
|
84
|
+
|
85
|
+
fragments.each do |f|
|
86
|
+
if base_schema.is_a?(JSON::Schema) #test if fragment is a JSON:Schema instance
|
87
|
+
if !base_schema.schema.has_key?(f)
|
88
|
+
raise JSON::Schema::SchemaError.new("Invalid fragment resolution for :fragment option")
|
89
|
+
end
|
90
|
+
base_schema = base_schema.schema[f]
|
91
|
+
elsif base_schema.is_a?(Hash)
|
92
|
+
if !base_schema.has_key?(f)
|
93
|
+
raise JSON::Schema::SchemaError.new("Invalid fragment resolution for :fragment option")
|
94
|
+
end
|
95
|
+
base_schema = JSON::Schema.new(base_schema[f],schema_uri,@options[:version])
|
96
|
+
elsif base_schema.is_a?(Array)
|
97
|
+
if base_schema[f.to_i].nil?
|
98
|
+
raise JSON::Schema::SchemaError.new("Invalid fragment resolution for :fragment option")
|
99
|
+
end
|
100
|
+
base_schema = JSON::Schema.new(base_schema[f.to_i],schema_uri,@options[:version])
|
101
|
+
else
|
102
|
+
raise JSON::Schema::SchemaError.new("Invalid schema encountered when resolving :fragment option")
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
if @options[:list]
|
107
|
+
base_schema.to_array_schema
|
108
|
+
else
|
109
|
+
base_schema
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# Run a simple true/false validation of data against a schema
|
114
|
+
def validate()
|
115
|
+
@base_schema.validate(@data,[],self,@validation_options)
|
116
|
+
if @options[:errors_as_objects]
|
117
|
+
return @errors.map{|e| e.to_hash}
|
118
|
+
else
|
119
|
+
return @errors.map{|e| e.to_string}
|
120
|
+
end
|
121
|
+
ensure
|
122
|
+
if @validation_options[:clear_cache] == true
|
123
|
+
Validator.clear_cache
|
124
|
+
end
|
125
|
+
if @validation_options[:insert_defaults]
|
126
|
+
JSON::Validator.merge_missing_values(@data, @original_data)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def load_ref_schema(parent_schema, ref)
|
131
|
+
schema_uri = absolutize_ref_uri(ref, parent_schema.uri)
|
132
|
+
|
133
|
+
return true if self.class.schema_loaded?(schema_uri)
|
134
|
+
|
135
|
+
schema = @options[:schema_reader].read(schema_uri)
|
136
|
+
self.class.add_schema(schema)
|
137
|
+
build_schemas(schema)
|
138
|
+
end
|
139
|
+
|
140
|
+
def absolutize_ref_uri(ref, parent_schema_uri)
|
141
|
+
ref_uri = Addressable::URI.parse(ref)
|
142
|
+
|
143
|
+
return ref_uri if ref_uri.absolute?
|
144
|
+
# This is a self reference and thus the schema does not need to be re-loaded
|
145
|
+
return parent_schema_uri if ref_uri.path.empty?
|
146
|
+
|
147
|
+
uri = parent_schema_uri.clone
|
148
|
+
uri.fragment = ''
|
149
|
+
Util::URI.normalized_uri(uri.join(ref_uri.path))
|
150
|
+
end
|
151
|
+
|
152
|
+
# Build all schemas with IDs, mapping out the namespace
|
153
|
+
def build_schemas(parent_schema)
|
154
|
+
schema = parent_schema.schema
|
155
|
+
|
156
|
+
# Build ref schemas if they exist
|
157
|
+
if schema["$ref"]
|
158
|
+
load_ref_schema(parent_schema, schema["$ref"])
|
159
|
+
end
|
160
|
+
|
161
|
+
case schema["extends"]
|
162
|
+
when String
|
163
|
+
load_ref_schema(parent_schema, schema["extends"])
|
164
|
+
when Array
|
165
|
+
schema['extends'].each do |type|
|
166
|
+
handle_schema(parent_schema, type)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
# Check for schemas in union types
|
171
|
+
["type", "disallow"].each do |key|
|
172
|
+
if schema[key].is_a?(Array)
|
173
|
+
schema[key].each do |type|
|
174
|
+
if type.is_a?(Hash)
|
175
|
+
handle_schema(parent_schema, type)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
# Schema properties whose values are objects, the values of which
|
182
|
+
# are themselves schemas.
|
183
|
+
%w[definitions properties patternProperties].each do |key|
|
184
|
+
next unless value = schema[key]
|
185
|
+
value.each do |k, inner_schema|
|
186
|
+
handle_schema(parent_schema, inner_schema)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
# Schema properties whose values are themselves schemas.
|
191
|
+
%w[additionalProperties additionalItems dependencies extends].each do |key|
|
192
|
+
next unless schema[key].is_a?(Hash)
|
193
|
+
handle_schema(parent_schema, schema[key])
|
194
|
+
end
|
195
|
+
|
196
|
+
# Schema properties whose values may be an array of schemas.
|
197
|
+
%w[allOf anyOf oneOf not].each do |key|
|
198
|
+
next unless value = schema[key]
|
199
|
+
Array(value).each do |inner_schema|
|
200
|
+
handle_schema(parent_schema, inner_schema)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
# Items are always schemas
|
205
|
+
if schema["items"]
|
206
|
+
items = schema["items"].clone
|
207
|
+
items = [items] unless items.is_a?(Array)
|
208
|
+
|
209
|
+
items.each do |item|
|
210
|
+
handle_schema(parent_schema, item)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
# Convert enum to a ArraySet
|
215
|
+
if schema["enum"].is_a?(Array)
|
216
|
+
schema["enum"] = ArraySet.new(schema["enum"])
|
217
|
+
end
|
218
|
+
|
219
|
+
end
|
220
|
+
|
221
|
+
# Either load a reference schema or create a new schema
|
222
|
+
def handle_schema(parent_schema, obj)
|
223
|
+
if obj.is_a?(Hash)
|
224
|
+
schema_uri = parent_schema.uri.clone
|
225
|
+
schema = JSON::Schema.new(obj, schema_uri, parent_schema.validator)
|
226
|
+
if obj['id']
|
227
|
+
Validator.add_schema(schema)
|
228
|
+
end
|
229
|
+
build_schemas(schema)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def validation_error(error)
|
234
|
+
@errors.push(error)
|
235
|
+
end
|
236
|
+
|
237
|
+
def validation_errors
|
238
|
+
@errors
|
239
|
+
end
|
240
|
+
|
241
|
+
|
242
|
+
class << self
|
243
|
+
def validate(schema, data,opts={})
|
244
|
+
begin
|
245
|
+
validator = JSON::Validator.new(schema, data, opts)
|
246
|
+
validator.validate
|
247
|
+
return true
|
248
|
+
rescue JSON::Schema::ValidationError, JSON::Schema::SchemaError
|
249
|
+
return false
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
def validate_json(schema, data, opts={})
|
254
|
+
validate(schema, data, opts.merge(:json => true))
|
255
|
+
end
|
256
|
+
|
257
|
+
def validate_uri(schema, data, opts={})
|
258
|
+
validate(schema, data, opts.merge(:uri => true))
|
259
|
+
end
|
260
|
+
|
261
|
+
def validate!(schema, data,opts={})
|
262
|
+
validator = JSON::Validator.new(schema, data, opts)
|
263
|
+
validator.validate
|
264
|
+
return true
|
265
|
+
end
|
266
|
+
alias_method 'validate2', 'validate!'
|
267
|
+
|
268
|
+
def validate_json!(schema, data, opts={})
|
269
|
+
validate!(schema, data, opts.merge(:json => true))
|
270
|
+
end
|
271
|
+
|
272
|
+
def validate_uri!(schema, data, opts={})
|
273
|
+
validate!(schema, data, opts.merge(:uri => true))
|
274
|
+
end
|
275
|
+
|
276
|
+
def fully_validate(schema, data, opts={})
|
277
|
+
opts[:record_errors] = true
|
278
|
+
validator = JSON::Validator.new(schema, data, opts)
|
279
|
+
validator.validate
|
280
|
+
end
|
281
|
+
|
282
|
+
def fully_validate_schema(schema, opts={})
|
283
|
+
data = schema
|
284
|
+
schema = JSON::Validator.validator_for_name(opts[:version]).metaschema
|
285
|
+
fully_validate(schema, data, opts)
|
286
|
+
end
|
287
|
+
|
288
|
+
def fully_validate_json(schema, data, opts={})
|
289
|
+
fully_validate(schema, data, opts.merge(:json => true))
|
290
|
+
end
|
291
|
+
|
292
|
+
def fully_validate_uri(schema, data, opts={})
|
293
|
+
fully_validate(schema, data, opts.merge(:uri => true))
|
294
|
+
end
|
295
|
+
|
296
|
+
def schema_reader
|
297
|
+
@@schema_reader ||= JSON::Schema::Reader.new
|
298
|
+
end
|
299
|
+
|
300
|
+
def schema_reader=(reader)
|
301
|
+
@@schema_reader = reader
|
302
|
+
end
|
303
|
+
|
304
|
+
def clear_cache
|
305
|
+
@@schemas = {} if @@cache_schemas == false
|
306
|
+
end
|
307
|
+
|
308
|
+
def schemas
|
309
|
+
@@schemas
|
310
|
+
end
|
311
|
+
|
312
|
+
def add_schema(schema)
|
313
|
+
@@schemas[schema_key_for(schema.uri)] ||= schema
|
314
|
+
end
|
315
|
+
|
316
|
+
def schema_for_uri(uri)
|
317
|
+
# We only store normalized uris terminated with fragment #, so we can try whether
|
318
|
+
# normalization can be skipped
|
319
|
+
@@schemas[uri] || @@schemas[schema_key_for(uri)]
|
320
|
+
end
|
321
|
+
|
322
|
+
def schema_loaded?(schema_uri)
|
323
|
+
!schema_for_uri(schema_uri).nil?
|
324
|
+
end
|
325
|
+
|
326
|
+
def schema_key_for(uri)
|
327
|
+
key = Util::URI.normalized_uri(uri).to_s
|
328
|
+
key.end_with?('#') ? key : "#{key}#"
|
329
|
+
end
|
330
|
+
|
331
|
+
def cache_schemas=(val)
|
332
|
+
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."
|
333
|
+
@@cache_schemas = val == true ? true : false
|
334
|
+
end
|
335
|
+
|
336
|
+
def validators
|
337
|
+
@@validators
|
338
|
+
end
|
339
|
+
|
340
|
+
def default_validator
|
341
|
+
@@default_validator
|
342
|
+
end
|
343
|
+
|
344
|
+
def validator_for_uri(schema_uri)
|
345
|
+
return default_validator unless schema_uri
|
346
|
+
u = Addressable::URI.parse(schema_uri)
|
347
|
+
validator = validators["#{u.scheme}://#{u.host}#{u.path}"]
|
348
|
+
if validator.nil?
|
349
|
+
raise JSON::Schema::SchemaError.new("Schema not found: #{schema_uri}")
|
350
|
+
else
|
351
|
+
validator
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
def validator_for_name(schema_name)
|
356
|
+
return default_validator unless schema_name
|
357
|
+
validator = validators_for_names([schema_name]).first
|
358
|
+
if validator.nil?
|
359
|
+
raise JSON::Schema::SchemaError.new("The requested JSON schema version is not supported")
|
360
|
+
else
|
361
|
+
validator
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
alias_method :validator_for, :validator_for_uri
|
366
|
+
|
367
|
+
def register_validator(v)
|
368
|
+
@@validators["#{v.uri.scheme}://#{v.uri.host}#{v.uri.path}"] = v
|
369
|
+
end
|
370
|
+
|
371
|
+
def register_default_validator(v)
|
372
|
+
@@default_validator = v
|
373
|
+
end
|
374
|
+
|
375
|
+
def register_format_validator(format, validation_proc, versions = ["draft1", "draft2", "draft3", "draft4"])
|
376
|
+
custom_format_validator = JSON::Schema::CustomFormat.new(validation_proc)
|
377
|
+
validators_for_names(versions).each do |validator|
|
378
|
+
validator.formats[format.to_s] = custom_format_validator
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
def deregister_format_validator(format, versions = ["draft1", "draft2", "draft3", "draft4"])
|
383
|
+
validators_for_names(versions).each do |validator|
|
384
|
+
validator.formats[format.to_s] = validator.default_formats[format.to_s]
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
def restore_default_formats(versions = ["draft1", "draft2", "draft3", "draft4"])
|
389
|
+
validators_for_names(versions).each do |validator|
|
390
|
+
validator.formats = validator.default_formats.clone
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
def json_backend
|
395
|
+
if defined?(MultiJson)
|
396
|
+
MultiJson.respond_to?(:adapter) ? MultiJson.adapter : MultiJson.engine
|
397
|
+
else
|
398
|
+
@@json_backend
|
399
|
+
end
|
400
|
+
end
|
401
|
+
|
402
|
+
def json_backend=(backend)
|
403
|
+
if defined?(MultiJson)
|
404
|
+
backend = backend == 'json' ? 'json_gem' : backend
|
405
|
+
MultiJson.respond_to?(:use) ? MultiJson.use(backend) : MultiJson.engine = backend
|
406
|
+
else
|
407
|
+
backend = backend.to_s
|
408
|
+
if @@available_json_backends.include?(backend)
|
409
|
+
@@json_backend = backend
|
410
|
+
else
|
411
|
+
raise JSON::Schema::JsonParseError.new("The JSON backend '#{backend}' could not be found.")
|
412
|
+
end
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
def parse(s)
|
417
|
+
if defined?(MultiJson)
|
418
|
+
MultiJson.respond_to?(:adapter) ? MultiJson.load(s) : MultiJson.decode(s)
|
419
|
+
else
|
420
|
+
case @@json_backend.to_s
|
421
|
+
when 'json'
|
422
|
+
JSON.parse(s, :quirks_mode => true)
|
423
|
+
when 'yajl'
|
424
|
+
json = StringIO.new(s)
|
425
|
+
parser = Yajl::Parser.new
|
426
|
+
parser.parse(json) or raise JSON::Schema::JsonParseError.new("The JSON could not be parsed by yajl")
|
427
|
+
else
|
428
|
+
raise JSON::Schema::JsonParseError.new("No supported JSON parsers found. The following parsers are suported:\n * yajl-ruby\n * json")
|
429
|
+
end
|
430
|
+
end
|
431
|
+
end
|
432
|
+
|
433
|
+
def merge_missing_values(source, destination)
|
434
|
+
case destination
|
435
|
+
when Hash
|
436
|
+
source.each do |key, source_value|
|
437
|
+
destination_value = destination[key] || destination[key.to_sym]
|
438
|
+
if destination_value.nil?
|
439
|
+
destination[key] = source_value
|
440
|
+
else
|
441
|
+
merge_missing_values(source_value, destination_value)
|
442
|
+
end
|
443
|
+
end
|
444
|
+
when Array
|
445
|
+
source.each_with_index do |source_value, i|
|
446
|
+
destination_value = destination[i]
|
447
|
+
merge_missing_values(source_value, destination_value)
|
448
|
+
end
|
449
|
+
end
|
450
|
+
end
|
451
|
+
|
452
|
+
if !defined?(MultiJson)
|
453
|
+
if Gem::Specification::find_all_by_name('json').any?
|
454
|
+
require 'json'
|
455
|
+
@@available_json_backends << 'json'
|
456
|
+
@@json_backend = 'json'
|
457
|
+
else
|
458
|
+
# Try force-loading json for rubies > 1.9.2
|
459
|
+
begin
|
460
|
+
require 'json'
|
461
|
+
@@available_json_backends << 'json'
|
462
|
+
@@json_backend = 'json'
|
463
|
+
rescue LoadError
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
|
468
|
+
if Gem::Specification::find_all_by_name('yajl-ruby').any?
|
469
|
+
require 'yajl'
|
470
|
+
@@available_json_backends << 'yajl'
|
471
|
+
@@json_backend = 'yajl'
|
472
|
+
end
|
473
|
+
|
474
|
+
if @@json_backend == 'yajl'
|
475
|
+
@@serializer = lambda{|o| Yajl::Encoder.encode(o) }
|
476
|
+
else
|
477
|
+
@@serializer = lambda{|o| YAML.dump(o) }
|
478
|
+
end
|
479
|
+
end
|
480
|
+
|
481
|
+
private
|
482
|
+
|
483
|
+
def validators_for_names(names)
|
484
|
+
names.map! { |name| name.to_s }
|
485
|
+
validators.reduce([]) do |memo, (_, validator)|
|
486
|
+
memo.tap { |m| m << validator if (validator.names & names).any? }
|
487
|
+
end
|
488
|
+
end
|
489
|
+
end
|
490
|
+
|
491
|
+
private
|
492
|
+
|
493
|
+
if Gem::Specification::find_all_by_name('uuidtools').any?
|
494
|
+
require 'uuidtools'
|
495
|
+
@@fake_uuid_generator = lambda{|s| UUIDTools::UUID.sha1_create(UUIDTools::UUID_URL_NAMESPACE, s).to_s }
|
496
|
+
else
|
497
|
+
require 'json-schema/util/uuid'
|
498
|
+
@@fake_uuid_generator = lambda{|s| JSON::Util::UUID.create_v5(s,JSON::Util::UUID::Nil).to_s }
|
499
|
+
end
|
500
|
+
|
501
|
+
def serialize schema
|
502
|
+
if defined?(MultiJson)
|
503
|
+
MultiJson.respond_to?(:dump) ? MultiJson.dump(schema) : MultiJson.encode(schema)
|
504
|
+
else
|
505
|
+
@@serializer.call(schema)
|
506
|
+
end
|
507
|
+
end
|
508
|
+
|
509
|
+
def fake_uuid schema
|
510
|
+
@@fake_uuid_generator.call(schema)
|
511
|
+
end
|
512
|
+
|
513
|
+
def initialize_schema(schema)
|
514
|
+
if schema.is_a?(String)
|
515
|
+
begin
|
516
|
+
# Build a fake URI for this
|
517
|
+
schema_uri = Addressable::URI.parse(fake_uuid(schema))
|
518
|
+
schema = JSON::Schema.new(JSON::Validator.parse(schema), schema_uri, @options[:version])
|
519
|
+
if @options[:list] && @options[:fragment].nil?
|
520
|
+
schema = schema.to_array_schema
|
521
|
+
end
|
522
|
+
Validator.add_schema(schema)
|
523
|
+
rescue
|
524
|
+
# Build a uri for it
|
525
|
+
schema_uri = Util::URI.normalized_uri(schema)
|
526
|
+
if !self.class.schema_loaded?(schema_uri)
|
527
|
+
schema = @options[:schema_reader].read(schema_uri)
|
528
|
+
schema = JSON::Schema.stringify(schema)
|
529
|
+
|
530
|
+
if @options[:list] && @options[:fragment].nil?
|
531
|
+
schema = schema.to_array_schema
|
532
|
+
end
|
533
|
+
|
534
|
+
Validator.add_schema(schema)
|
535
|
+
else
|
536
|
+
schema = self.class.schema_for_uri(schema_uri)
|
537
|
+
if @options[:list] && @options[:fragment].nil?
|
538
|
+
schema = schema.to_array_schema
|
539
|
+
schema.uri = Addressable::URI.parse(fake_uuid(serialize(schema.schema)))
|
540
|
+
Validator.add_schema(schema)
|
541
|
+
end
|
542
|
+
schema
|
543
|
+
end
|
544
|
+
end
|
545
|
+
elsif schema.is_a?(Hash)
|
546
|
+
schema_uri = Addressable::URI.parse(fake_uuid(serialize(schema)))
|
547
|
+
schema = JSON::Schema.stringify(schema)
|
548
|
+
schema = JSON::Schema.new(schema, schema_uri, @options[:version])
|
549
|
+
if @options[:list] && @options[:fragment].nil?
|
550
|
+
schema = schema.to_array_schema
|
551
|
+
end
|
552
|
+
Validator.add_schema(schema)
|
553
|
+
else
|
554
|
+
raise "Invalid schema - must be either a string or a hash"
|
555
|
+
end
|
556
|
+
|
557
|
+
schema
|
558
|
+
end
|
559
|
+
|
560
|
+
def initialize_data(data)
|
561
|
+
if @options[:parse_data]
|
562
|
+
if @options[:json]
|
563
|
+
data = JSON::Validator.parse(data)
|
564
|
+
elsif @options[:uri]
|
565
|
+
json_uri = Util::URI.normalized_uri(data)
|
566
|
+
data = JSON::Validator.parse(custom_open(json_uri))
|
567
|
+
elsif data.is_a?(String)
|
568
|
+
begin
|
569
|
+
data = JSON::Validator.parse(data)
|
570
|
+
rescue
|
571
|
+
begin
|
572
|
+
json_uri = Util::URI.normalized_uri(data)
|
573
|
+
data = JSON::Validator.parse(custom_open(json_uri))
|
574
|
+
rescue
|
575
|
+
# Silently discard the error - the data will not change
|
576
|
+
end
|
577
|
+
end
|
578
|
+
end
|
579
|
+
end
|
580
|
+
JSON::Schema.stringify(data)
|
581
|
+
end
|
582
|
+
|
583
|
+
def custom_open(uri)
|
584
|
+
uri = Util::URI.normalized_uri(uri) if uri.is_a?(String)
|
585
|
+
if uri.absolute? && uri.scheme != 'file'
|
586
|
+
open(uri.to_s).read
|
587
|
+
else
|
588
|
+
File.read(Addressable::URI.unescape(uri.path))
|
589
|
+
end
|
590
|
+
end
|
591
|
+
end
|
592
|
+
end
|