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 +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
|