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 +4 -4
- data/CHANGELOG.md +10 -0
- data/README.md +11 -2
- data/lib/xsd/base_object.rb +20 -10
- data/lib/xsd/objects/extension.rb +20 -1
- data/lib/xsd/objects/import.rb +7 -7
- data/lib/xsd/objects/include.rb +29 -0
- data/lib/xsd/objects/restriction.rb +20 -0
- data/lib/xsd/objects/schema.rb +114 -13
- data/lib/xsd/version.rb +1 -1
- data/lib/xsd/xml.rb +15 -11
- data/lib/xsd.rb +1 -1
- metadata +3 -3
- data/lib/xsd/validator.rb +0 -90
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ab9eaf4eb6b8aacde5379acdc08cc16c3d85e3dc9d75a5887b2f1db05ea6ed58
|
4
|
+
data.tar.gz: 8c5fdbf108a6867689ff3292a5e208a33160f232b4a3d3a96eb55e42e9b6da7e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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
|
data/lib/xsd/base_object.rb
CHANGED
@@ -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
|
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
|
-
|
115
|
+
search_schemas = schemas_for_namespace(name_prefix)
|
116
116
|
|
117
117
|
# find element in target schema
|
118
|
-
|
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
|
-
|
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
|
data/lib/xsd/objects/import.rb
CHANGED
@@ -16,23 +16,23 @@ module XSD
|
|
16
16
|
property :schemaLocation, :string
|
17
17
|
|
18
18
|
# Get imported schema
|
19
|
-
# @return
|
19
|
+
# @return Schema
|
20
20
|
def imported_schema
|
21
|
-
|
22
|
-
return
|
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
|
-
|
29
|
+
new_schema = reader.add_schema_xml(xml)
|
30
30
|
|
31
|
-
unless namespace ==
|
32
|
-
raise ImportError, 'Import
|
31
|
+
unless namespace == new_schema.target_namespace
|
32
|
+
raise ImportError, 'Import namespace does not match imported schema targetNamespace'
|
33
33
|
end
|
34
34
|
|
35
|
-
|
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
|
data/lib/xsd/objects/schema.rb
CHANGED
@@ -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
|
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.
|
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
|
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
|
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
|
144
|
+
return [] if %i[import include].include?(name.to_sym)
|
137
145
|
|
138
|
-
imports.map do |import|
|
139
|
-
|
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[
|
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
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
|
-
#
|
96
|
-
# @return
|
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
|
-
#
|
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
|
-
|
110
|
-
schemas.push(
|
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
|
-
# @
|
130
|
-
|
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.
|
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.
|
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-
|
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
|