xsd 2.0.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ee4b9e2059eeb7c7109e687ca866c3617514e167f2d93de816d452fdf01be85b
4
- data.tar.gz: ee5451af6e745dbdfffc55fa26329fd2c68b3257699a6e65fd923da3278769ea
3
+ metadata.gz: ab9eaf4eb6b8aacde5379acdc08cc16c3d85e3dc9d75a5887b2f1db05ea6ed58
4
+ data.tar.gz: 8c5fdbf108a6867689ff3292a5e208a33160f232b4a3d3a96eb55e42e9b6da7e
5
5
  SHA512:
6
- metadata.gz: c937c9501cac27b7d1a003d34feec5fc927663656793c0e2a4abb62112f7b36ee7001532eccc1f1557913c23355feddab616a9df493fe2c7556fb3eaa74d50ce
7
- data.tar.gz: 588d4649a016ed883a401447435a3a01512a610d1bc5b46b8abae0d6b0367ffe2369073f4f0f8bdb834643321f2a22eda196d89315d6e84f3b0222f4eee0c622
6
+ metadata.gz: d75af5531e7763eb3bc9359b2992e03704d963cfbc394a863022a3ff8db87d97db368409666bc7540f2fc772b78e2b25ab59905301e19d370827750050823462
7
+ data.tar.gz: 2b06fd643f69e41017683b689e614dbef7c42dca4fefbdec753f7f8edcc0978caf80eefe8c7d68e4acdc86aadd2777160a5cd7105a776d13ebfc2c57fc5af45c
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [2.2.0] - 2023-07-10
4
+
5
+ - Fixed built-in types detection for unprefixed schema namespace
6
+ - Move validation to Schema object
7
+
8
+ ## [2.1.0] - 2023-07-10
9
+
10
+ - Add support for <xs:include> element
11
+ - Changed method "schema_for_namespace(namespace) -> XSD::Schema" signature to "schemas_for_namespace(namespace) -> Array<XSD::Schema>"
12
+
3
13
  ## [2.0.0] - 2023-07-07
4
14
 
5
15
  - Change XSD::XML.new(file, **options) -> XSD::XML.open(file, **options)
data/README.md CHANGED
@@ -28,20 +28,29 @@ require 'xsd'
28
28
  # Load ruby-xsd
29
29
  reader = XSD::XML.open('some.xsd')
30
30
 
31
+ # Get elements and their child elements
32
+ element = reader['NewReleaseMessage']
33
+ element.collect_elements.map(&:name) # => ['MessageHeader', 'UpdateIndicator', 'IsBackfill', 'CatalogTransfer', 'WorkList', 'CueSheetList', 'ResourceList', 'CollectionList', 'ReleaseList', 'DealList']
34
+
31
35
  # Get attributes
32
36
  attribute = reader['NewReleaseMessage']['@MessageSchemaVersionId']
33
37
 
34
- # Get atrribute information
38
+ # Get attribute information
35
39
  attribute.name # => 'MessageSchemaVersionId'
36
40
  attribute.type # => 'xs:string'
37
41
  attribute.required? # => true
42
+ attribute.optional? # => false
43
+ attribute.prohibited? # => true
38
44
 
39
45
  # Get element information
40
- element = reader['NewReleaseMessage']['ResourceList']['SoundRecording']
46
+ element = reader['NewReleaseMessage']['ResourceList']['SoundRecording'] # => XSD::Element
41
47
  element.min_occurs # => 0
42
48
  element.max_occurs # => :unbounded
43
49
  element.type # => 'ern:SoundRecording'
44
50
  element.complex_type # => XSD::ComplexType
51
+ element.complex? # => true
52
+ element.multiple_allowed? # => true
53
+ element.required? # => false
45
54
  ```
46
55
 
47
56
  ## Development
@@ -67,13 +67,13 @@ module XSD
67
67
  end
68
68
 
69
69
  # Get schema by namespace or namespace prefix
70
- # @param [String] namespace
71
- # @return Schema
72
- def schema_for_namespace(namespace)
70
+ # @param [String, nil] namespace
71
+ # @return Array<Schema>
72
+ def schemas_for_namespace(namespace)
73
73
  if schema.targets_namespace?(namespace)
74
- schema
74
+ [schema, *schema.includes.map(&:imported_schema)]
75
75
  elsif (import = schema.import_by_namespace(namespace))
76
- import.imported_schema
76
+ [import.imported_schema]
77
77
  else
78
78
  raise Error, "Schema not found for namespace '#{namespace}' in '#{schema.id || schema.target_namespace}'"
79
79
  end
@@ -112,12 +112,15 @@ module XSD
112
112
  return nil if schema.namespace_prefix == name_prefix
113
113
 
114
114
  # determine schema for namespace
115
- search_schema = schema_for_namespace(name_prefix)
115
+ search_schemas = schemas_for_namespace(name_prefix)
116
116
 
117
117
  # find element in target schema
118
- result = search_schema.node.xpath("./xs:#{node_name}[@name=\"#{name_local}\"]", { 'xs' => XML_SCHEMA }).first
118
+ search_schemas.each do |s|
119
+ node = s.node.xpath("./xs:#{node_name}[@name=\"#{name_local}\"]", { 'xs' => XML_SCHEMA }).first
120
+ return s.node_to_object(node) if node
121
+ end
119
122
 
120
- result ? search_schema.node_to_object(result) : nil
123
+ nil
121
124
  end
122
125
 
123
126
  # Get reader object for node
@@ -169,9 +172,9 @@ module XSD
169
172
 
170
173
  # Get namespace prefix from node name
171
174
  # @param [String, nil] name Name to strip from
172
- # @return String
175
+ # @return String, nil
173
176
  def get_prefix(name)
174
- name&.include?(':') ? name.split(':').first : ''
177
+ name&.include?(':') ? name.split(':').first : nil
175
178
  end
176
179
 
177
180
  # Return element documentation
@@ -343,5 +346,12 @@ module XSD
343
346
  name.to_sym
344
347
  end
345
348
  end
349
+
350
+ # Return string if it is not empty, or nil otherwise
351
+ # @param [String, nil] string
352
+ # @return String, nil
353
+ def nil_if_empty(string)
354
+ string&.empty? ? nil : string
355
+ end
346
356
  end
347
357
  end
@@ -8,7 +8,26 @@ module XSD
8
8
  TYPE_PROPERTY = nil
9
9
 
10
10
  include Based
11
- include SimpleTyped
12
11
  include AttributeContainer
12
+
13
+ # Nested group
14
+ # @!attribute group
15
+ # @return Group
16
+ child :group, :group
17
+
18
+ # Nested all
19
+ # @!attribute all
20
+ # @return All
21
+ child :all, :all
22
+
23
+ # Nested choice
24
+ # @!attribute choice
25
+ # @return Choice
26
+ child :choice, :choice
27
+
28
+ # Nested sequence
29
+ # @!attribute sequence
30
+ # @return Sequence
31
+ child :sequence, :sequence
13
32
  end
14
33
  end
@@ -16,23 +16,23 @@ module XSD
16
16
  property :schemaLocation, :string
17
17
 
18
18
  # Get imported schema
19
- # @return XSD:Schema
19
+ # @return Schema
20
20
  def imported_schema
21
- schema = reader.schema_for_namespace(namespace)
22
- return schema if schema
21
+ known_schemas = reader.schemas_for_namespace(namespace)
22
+ return known_schemas.first if known_schemas.any?
23
23
 
24
24
  unless schema_location
25
25
  raise ImportError, "Schema location not provided for namespace '#{namespace}', use add_schema_xml()/add_schema_node()"
26
26
  end
27
27
 
28
28
  xml = reader.resource_resolver.call(schema_location, namespace)
29
- schema = reader.add_schema_xml(xml)
29
+ new_schema = reader.add_schema_xml(xml)
30
30
 
31
- unless namespace == schema.target_namespace
32
- raise ImportError, 'Import location does not match imported schema targetNamespace'
31
+ unless namespace == new_schema.target_namespace
32
+ raise ImportError, 'Import namespace does not match imported schema targetNamespace'
33
33
  end
34
34
 
35
- schema
35
+ new_schema
36
36
  end
37
37
  end
38
38
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module XSD
4
+ # The include element is used to add multiple schemas with the same target namespace to a document.
5
+ # Parent elements: schema
6
+ # https://www.w3schools.com/xml/el_include.asp
7
+ class Include < BaseObject
8
+ # Required. Specifies the URI to the schema to include in the target namespace of the containing schema
9
+ # @!attribute schema_location
10
+ # @return String
11
+ property :schemaLocation, :string
12
+
13
+ # Get imported schema
14
+ # @return Schema
15
+ def imported_schema
16
+ # cache included schema locally as it has no unique namespace to check in global registry
17
+ return @imported_schema if @imported_schema
18
+
19
+ xml = reader.resource_resolver.call(schema_location)
20
+ new_schema = reader.add_schema_xml(xml)
21
+
22
+ unless schema.target_namespace == new_schema.target_namespace
23
+ raise ImportError, 'Schema targetNamespace does not match included schema targetNamespace'
24
+ end
25
+
26
+ @imported_schema = new_schema
27
+ end
28
+ end
29
+ end
@@ -16,6 +16,26 @@ module XSD
16
16
  fractionDigits length minLength maxLength enumeration whiteSpace pattern
17
17
  ].freeze
18
18
 
19
+ # Nested group
20
+ # @!attribute group
21
+ # @return Group
22
+ child :group, :group
23
+
24
+ # Nested all
25
+ # @!attribute all
26
+ # @return All
27
+ child :all, :all
28
+
29
+ # Nested choice
30
+ # @!attribute choice
31
+ # @return Choice
32
+ child :choice, :choice
33
+
34
+ # Nested sequence
35
+ # @!attribute sequence
36
+ # @return Sequence
37
+ child :sequence, :sequence
38
+
19
39
  # Get restriction facets
20
40
  # @return Hash
21
41
  def facets
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'tmpdir'
4
+ require 'securerandom'
5
+
3
6
  module XSD
4
7
  # The schema element defines the root element of a schema.
5
8
  # Parent elements: NONE
@@ -85,6 +88,11 @@ module XSD
85
88
  # @return Array<Import>
86
89
  child :imports, [:import]
87
90
 
91
+ # Schema includes
92
+ # @!attribute includes
93
+ # @return Array<Include>
94
+ child :includes, [:include]
95
+
88
96
  # Get current schema object
89
97
  # @return Schema
90
98
  def schema
@@ -103,43 +111,44 @@ module XSD
103
111
  attributes
104
112
  end
105
113
 
106
- # Get target namespace prefix. There may be more than one prefix, but we return only first defined
107
- # @return String
114
+ # Get target namespace prefix
115
+ # @return String, nil
108
116
  def target_namespace_prefix
109
- @target_namespace_prefix ||= namespaces.key(target_namespace)&.sub(/^xmlns:?/, '') || ''
117
+ nil_if_empty(@target_namespace_prefix ||= namespaces.key(target_namespace)&.sub(/^xmlns:?/, '') || '')
110
118
  end
111
119
 
112
120
  # Get schema namespace prefix
113
- # @return String
121
+ # @return String, nil
114
122
  def namespace_prefix
115
- @namespace_prefix ||= namespaces.key(XML_SCHEMA).sub(/^xmlns:?/, '')
123
+ nil_if_empty(@namespace_prefix ||= namespaces.key(XML_SCHEMA).sub(/^xmlns:?/, ''))
116
124
  end
117
125
 
118
126
  # Check if namespace is a target namespace
119
- # @param [String] namespace
127
+ # @param [String, nil] namespace
120
128
  # @return Boolean
121
129
  def targets_namespace?(namespace)
122
- namespace == target_namespace || namespaces[namespace.empty? ? 'xmlns' : "xmlns:#{namespace}"] == target_namespace
130
+ namespace == target_namespace || namespaces[namespace.nil? ? 'xmlns' : "xmlns:#{namespace}"] == target_namespace
123
131
  end
124
132
 
125
- # Override map_children on schema to get objects from all imported schemas
133
+ # Override map_children on schema to get objects from all loaded schemas
126
134
  # @param [Symbol] name
127
135
  # @return Array<BaseObject>
128
136
  def map_children(name, cache = {})
129
137
  super(name) + import_map_children(name, cache)
130
138
  end
131
139
 
132
- # Get children from all imported schemas
140
+ # Get children from all loaded schemas
133
141
  # @param [Symbol] name
134
142
  # @return Array<BaseObject>
135
143
  def import_map_children(name, cache)
136
- return [] if name.to_sym == :import
144
+ return [] if %i[import include].include?(name.to_sym)
137
145
 
138
- imports.map do |import|
139
- if cache[import.namespace]
146
+ (includes + imports).map do |import|
147
+ key = import.respond_to?(:namespace) && import.namespace ? import.namespace : import.schema_location
148
+ if cache.key?(key)
140
149
  nil
141
150
  else
142
- cache[import.namespace] = true
151
+ cache[key] = true
143
152
  import.imported_schema.map_children(name, cache)
144
153
  end
145
154
  end.compact.flatten
@@ -151,5 +160,97 @@ module XSD
151
160
  aliases = [ns, namespaces["xmlns:#{(ns || '').gsub(/^xmlns:/, '')}"], reader.namespace_prefixes[ns]].compact
152
161
  imports.find { |import| aliases.include?(import.namespace) }
153
162
  end
163
+
164
+ # Validate XML against current schema
165
+ # @param [String, Pathname, Nokogiri::XML::Document] xml
166
+ def validate_xml(xml)
167
+ # validate input
168
+ raise ValidationError unless xml.is_a?(Nokogiri::XML::Document) || xml.is_a?(Pathname) || xml.is_a?(String)
169
+
170
+ begin
171
+ document = xml.is_a?(Nokogiri::XML::Document) ? xml : Nokogiri::XML(xml)
172
+ rescue Nokogiri::XML::SyntaxError => e
173
+ raise ValidationError, e
174
+ end
175
+
176
+ errors = schema_validator.validate(document)
177
+ raise ValidationError, errors.map(&:message).join('; ') if errors.any?
178
+ end
179
+
180
+ # Validate current schema against XMLSchema 1.0
181
+ def validate
182
+ begin
183
+ schema_validator
184
+ rescue Nokogiri::XML::SyntaxError => e
185
+ # TODO: display import map name for imported_xsd
186
+ message = e.message + (e.file ? " in file '#{File.basename(e.file)}'" : '')
187
+ raise ValidationError, message
188
+ end
189
+ end
190
+
191
+ private
192
+
193
+ # Get Nokogiri::XML::Schema object to validate against
194
+ # @return Nokogiri::XML::Schema
195
+ def schema_validator
196
+ return @schema_validator if @schema_validator
197
+
198
+ # if !imported_xsd.empty?
199
+ # imports are explicitly provided - put all files in one tmpdir and update import paths appropriately
200
+ # TODO: save file/path map to display in errors
201
+ Dir.mktmpdir('XSD', reader.tmp_dir) do |dir|
202
+ # create primary xsd file
203
+ file = "#{::SecureRandom.urlsafe_base64}.xsd"
204
+
205
+ # create imported xsd files
206
+ recursive_import_xsd(self, file, Set.new) do |f, data|
207
+ File.write("#{dir}/#{f}", data)
208
+ end
209
+
210
+ # read schema from tmp file descriptor
211
+ io = File.open("#{dir}/#{file}")
212
+ @schema_validator = create_xml_schema(io)
213
+ end
214
+ # else
215
+ # @schema_validator = create_xml_schema(schema.node.to_xml)
216
+ # end
217
+
218
+ @schema_validator
219
+ end
220
+
221
+ # Сформировать имена файлов и содержимое XSD схем для корректной валидации
222
+ # @param [Schema] schema
223
+ # @param [String] file
224
+ # @param [Set] processed
225
+ def recursive_import_xsd(schema, file, processed, &block)
226
+ # handle recursion
227
+ namespace = schema.target_namespace
228
+ return if processed.include?(namespace)
229
+
230
+ processed.add(namespace)
231
+
232
+ data = schema.node.to_xml
233
+
234
+ schema.imports.each do |import|
235
+ name = "#{::SecureRandom.urlsafe_base64}.xsd"
236
+ location = import.schema_location
237
+ namespace = import.namespace
238
+
239
+ if location
240
+ data = data.sub("schemaLocation=\"#{location}\"", "schemaLocation=\"#{name}\"")
241
+ else
242
+ data = data.sub("namespace=\"#{namespace}\"", "namespace=\"#{namespace}\" schemaLocation=\"#{name}\"")
243
+ end
244
+ recursive_import_xsd(import.imported_schema, name, processed, &block)
245
+ end
246
+
247
+ block.call(file, data)
248
+ end
249
+
250
+ # Create Nokogiri XML Schema instance with preconfigured options
251
+ # @param [IO, String] io
252
+ def create_xml_schema(io)
253
+ Nokogiri::XML::Schema(io, Nokogiri::XML::ParseOptions.new.nononet)
254
+ end
154
255
  end
155
256
  end
data/lib/xsd/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module XSD
4
- VERSION = '2.0.0'
4
+ VERSION = '2.2.0'
5
5
  end
data/lib/xsd/xml.rb CHANGED
@@ -6,7 +6,6 @@ require 'net/http'
6
6
  module XSD
7
7
  class XML
8
8
  include Generator
9
- include Validator
10
9
 
11
10
  attr_reader :options, :object_cache, :schemas, :namespace_prefixes
12
11
 
@@ -31,6 +30,7 @@ module XSD
31
30
  'complexContent' => ComplexContent,
32
31
  'extension' => Extension,
33
32
  'import' => Import,
33
+ 'include' => Include,
34
34
  'simpleType' => SimpleType,
35
35
  'all' => All,
36
36
  'restriction' => Restriction,
@@ -63,6 +63,10 @@ module XSD
63
63
  'pattern' => Facet
64
64
  }.freeze
65
65
 
66
+ # Create reader from a file path
67
+ # @param [String] path
68
+ # @param [Hash] options
69
+ # @return XML
66
70
  def self.open(path, **options)
67
71
  reader = new(**options)
68
72
  reader.add_schema_xml(File.read(path))
@@ -92,8 +96,8 @@ module XSD
92
96
  Nokogiri::XML(xml)
93
97
  end
94
98
 
95
- # Get imported schema
96
- # @return XSD:Schema
99
+ # Add schema xml to reader instance
100
+ # @return Schema
97
101
  def add_schema_xml(xml)
98
102
  doc = read_document(xml)
99
103
  raise Error, 'Schema node not found, xml does not seem to be a valid XSD' unless doc.root&.name == 'schema'
@@ -101,15 +105,14 @@ module XSD
101
105
  add_schema_node(doc.root)
102
106
  end
103
107
 
104
- # Get imported schema
108
+ # Add schema node to reader instance
105
109
  # @return Schema
106
110
  def add_schema_node(node)
107
111
  raise Error, 'Added schema must be of type Nokogiri::XML::Node' unless node.is_a?(Nokogiri::XML::Node)
108
112
 
109
- schema = Schema.new(self.options.merge(node: node, reader: self))
110
- schemas.push(schema)
111
-
112
- schema
113
+ new_schema = Schema.new(options.merge(node: node, reader: self))
114
+ schemas.push(new_schema)
115
+ new_schema
113
116
  end
114
117
 
115
118
  # Add prefixes defined outside of processed schemas, for example in WSDL document
@@ -126,10 +129,11 @@ module XSD
126
129
  end
127
130
 
128
131
  # Get schema by namespace or namespace prefix
129
- # @return Schema, nil
130
- def schema_for_namespace(namespace)
132
+ # @param [String, nil] namespace
133
+ # @return Array<Schema>
134
+ def schemas_for_namespace(namespace)
131
135
  namespace = namespace_prefixes[namespace] if namespace_prefixes.key?(namespace)
132
- schemas.find { |schema| schema.target_namespace == namespace }
136
+ schemas.select { |schema| schema.target_namespace == namespace }
133
137
  end
134
138
 
135
139
  def [](*args)
data/lib/xsd.rb CHANGED
@@ -18,6 +18,7 @@ require 'xsd/objects/simple_content'
18
18
  require 'xsd/objects/complex_content'
19
19
  require 'xsd/objects/extension'
20
20
  require 'xsd/objects/import'
21
+ require 'xsd/objects/include'
21
22
  require 'xsd/objects/restriction'
22
23
  require 'xsd/objects/group'
23
24
  require 'xsd/objects/all'
@@ -37,6 +38,5 @@ require 'xsd/objects/any_attribute'
37
38
  require 'xsd/objects/key'
38
39
  require 'xsd/objects/keyref'
39
40
  require 'xsd/generator'
40
- require 'xsd/validator'
41
41
  require 'xsd/exceptions'
42
42
  require 'xsd/xml'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: xsd
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - d.arkhipov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-07-07 00:00:00.000000000 Z
11
+ date: 2023-07-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: builder
@@ -90,6 +90,7 @@ files:
90
90
  - lib/xsd/objects/field.rb
91
91
  - lib/xsd/objects/group.rb
92
92
  - lib/xsd/objects/import.rb
93
+ - lib/xsd/objects/include.rb
93
94
  - lib/xsd/objects/key.rb
94
95
  - lib/xsd/objects/keyref.rb
95
96
  - lib/xsd/objects/list.rb
@@ -108,7 +109,6 @@ files:
108
109
  - lib/xsd/shared/min_max_occurs.rb
109
110
  - lib/xsd/shared/referenced.rb
110
111
  - lib/xsd/shared/simple_typed.rb
111
- - lib/xsd/validator.rb
112
112
  - lib/xsd/version.rb
113
113
  - lib/xsd/xml.rb
114
114
  - xsd.gemspec
data/lib/xsd/validator.rb DELETED
@@ -1,90 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module XSD
4
- module Validator
5
- # Validate XML against XSD
6
- # @param [String, Pathname, Nokogiri::XML::Document] xml
7
- def validate_xml(xml)
8
- # validate input
9
- raise ValidationError unless xml.is_a?(Nokogiri::XML::Document) || xml.is_a?(Pathname) || xml.is_a?(String)
10
-
11
- begin
12
- document = xml.is_a?(Nokogiri::XML::Document) ? xml : Nokogiri::XML(xml)
13
- rescue Nokogiri::XML::SyntaxError => e
14
- raise ValidationError, e
15
- end
16
-
17
- errors = schema_validator.validate(document)
18
- raise ValidationError, errors.map(&:message).join('; ') if errors.any?
19
- end
20
-
21
- # Validate XSD against another XSD (by default uses XMLSchema 1.0)
22
- def validate
23
- begin
24
- schema_validator
25
- rescue Nokogiri::XML::SyntaxError => e
26
- # TODO: display import map name for imported_xsd
27
- message = e.message + (e.file ? " in file '#{File.basename(e.file)}'" : '')
28
- raise ValidationError, message
29
- end
30
- end
31
-
32
- private
33
-
34
- # Get Nokogiri::XML::Schema object to validate against
35
- # @return Nokogiri::XML::Schema
36
- def schema_validator
37
- return @schema_validator if @schema_validator
38
-
39
- if !imported_xsd.empty?
40
- # imports are explicitly provided - put all files in one tmpdir and update import paths appropriately
41
- # TODO: save file/path map to display in errors
42
- Dir.mktmpdir('XSD', tmp_dir) do |dir|
43
- # create primary xsd file
44
- file = "#{SecureRandom.urlsafe_base64}.xsd"
45
-
46
- # create imported xsd files
47
- recursive_import_xsd(self, file, Set.new) do |f, data|
48
- File.write("#{dir}/#{f}", data)
49
- end
50
-
51
- # read schema from tmp file descriptor
52
- io = File.open("#{dir}/#{file}")
53
- @schema_validator = create_xml_schema(io)
54
- end
55
- else
56
- @schema_validator = create_xml_schema(self.xsd)
57
- end
58
-
59
- @schema_validator
60
- end
61
-
62
- # Сформировать имена файлов и содержимое XSD схем для корректной валидации
63
- # @param [XML] reader
64
- # @param [String] file
65
- # @param [Set] processed
66
- def recursive_import_xsd(reader, file, processed, &block)
67
- # handle recursion
68
- namespace = reader.schema.target_namespace
69
- return if processed.include?(namespace)
70
-
71
- processed.add(namespace)
72
-
73
- data = reader.xml
74
-
75
- reader.imports.each do |import|
76
- name = "#{SecureRandom.urlsafe_base64}.xsd"
77
- data = data.sub("schemaLocation=\"#{import.schema_location}\"", "schemaLocation=\"#{name}\"")
78
- recursive_import_xsd(import.imported_reader, name, processed, &block)
79
- end
80
-
81
- block.call(file, data)
82
- end
83
-
84
- # Create Nokogiri XML Schema instance with preconfigured options
85
- # @param [IO] io
86
- def create_xml_schema(io)
87
- Nokogiri::XML::Schema(io, Nokogiri::XML::ParseOptions.new.nononet)
88
- end
89
- end
90
- end