xsd 2.0.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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