xsd 2.4.2 → 2.6.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 +11 -1
- data/README.md +10 -2
- data/lib/xsd/base_object.rb +71 -43
- data/lib/xsd/objects/attribute.rb +13 -2
- data/lib/xsd/objects/attribute_group.rb +3 -2
- data/lib/xsd/objects/complex_type.rb +52 -1
- data/lib/xsd/objects/element.rb +40 -9
- data/lib/xsd/objects/simple_type.rb +23 -2
- data/lib/xsd/shared/named.rb +17 -0
- data/lib/xsd/version.rb +1 -1
- data/lib/xsd/xml.rb +0 -2
- data/lib/xsd.rb +1 -1
- metadata +3 -3
- data/lib/xsd/generator.rb +0 -146
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2a5aa03e831e5cf4e88780b18a4ef57217eff54a14e2138669672f33486ba8bd
|
4
|
+
data.tar.gz: b6a9ab392e47192a6a286ffa7dc97c5f624c78d9ab117bdea67e11f801c39363
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a031bc3a3947708a8f6f8e4add4040853f9f8b62cd3465de56421600d9fc2d2b7c45b163efe239c7486dce05bf0427d8f380e119673b644739f736b4598b5276
|
7
|
+
data.tar.gz: ad7bbd0268373292a291935599749dc9937784438b398958acf592db4e1648967106f45c1dd402c82f3cdd534f2fe0e177a1250e91f65872fc3aa9d9a8b416a5
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,14 @@
|
|
1
|
-
## [
|
1
|
+
## [2.6.0] - 2024-04-12
|
2
|
+
|
3
|
+
- Fix XSD boolean attributes
|
4
|
+
- Add support for element lookup with namespace
|
5
|
+
- Add element (with simple content) data type calculation
|
6
|
+
- Add element mixed type calculation
|
7
|
+
|
8
|
+
## [2.4.3] - 2024-04-04
|
9
|
+
|
10
|
+
- Fix detecting types and refs when namespaces are overidden per element
|
11
|
+
- Fix documentation encoding issue with no XML declaration
|
2
12
|
|
3
13
|
## [2.4.1] - 2023-11-13
|
4
14
|
|
data/README.md
CHANGED
@@ -28,8 +28,13 @@ require 'xsd'
|
|
28
28
|
# Load ruby-xsd
|
29
29
|
reader = XSD::XML.open('some.xsd')
|
30
30
|
|
31
|
-
# Get
|
31
|
+
# Get element by name with hash lookup
|
32
32
|
element = reader['NewReleaseMessage']
|
33
|
+
|
34
|
+
# Get element by namespace + name with hash lookup
|
35
|
+
element = reader['{http://ddex.net/xml/ern/36}NewReleaseMessage']
|
36
|
+
|
37
|
+
# Get element child elements
|
33
38
|
element.collect_elements.map(&:name) # => ['MessageHeader', 'UpdateIndicator', 'IsBackfill', 'CatalogTransfer', 'WorkList', 'CueSheetList', 'ResourceList', 'CollectionList', 'ReleaseList', 'DealList']
|
34
39
|
|
35
40
|
# Get attributes
|
@@ -38,6 +43,7 @@ attribute = reader['NewReleaseMessage']['@MessageSchemaVersionId']
|
|
38
43
|
# Get attribute information
|
39
44
|
attribute.name # => 'MessageSchemaVersionId'
|
40
45
|
attribute.type # => 'xs:string'
|
46
|
+
attribute.data_type # => 'string'
|
41
47
|
attribute.required? # => true
|
42
48
|
attribute.optional? # => false
|
43
49
|
attribute.prohibited? # => true
|
@@ -48,7 +54,9 @@ element.min_occurs # => 0
|
|
48
54
|
element.max_occurs # => :unbounded
|
49
55
|
element.type # => 'ern:SoundRecording'
|
50
56
|
element.complex_type # => XSD::ComplexType
|
51
|
-
element.
|
57
|
+
element.complex_content? # => true
|
58
|
+
element.simple_content? # => false
|
59
|
+
element.mixed_content? # => false
|
52
60
|
element.multiple_allowed? # => true
|
53
61
|
element.required? # => false
|
54
62
|
```
|
data/lib/xsd/base_object.rb
CHANGED
@@ -34,7 +34,7 @@ module XSD
|
|
34
34
|
|
35
35
|
def initialize(options = {})
|
36
36
|
@options = options
|
37
|
-
@cache
|
37
|
+
@cache = {}
|
38
38
|
|
39
39
|
raise Error, "#{self.class}.new expects a hash parameter" unless @options.is_a?(Hash)
|
40
40
|
end
|
@@ -76,12 +76,16 @@ module XSD
|
|
76
76
|
# @param [String, nil] ns_or_prefix
|
77
77
|
# @return Array<Schema>
|
78
78
|
def schemas_for_namespace(ns_or_prefix)
|
79
|
-
if
|
79
|
+
# resolve namespace for current node if prefix was provided
|
80
|
+
prefix = node.namespaces["xmlns:#{ns_or_prefix}"]
|
81
|
+
ns = prefix || ns_or_prefix
|
82
|
+
|
83
|
+
if schema.targets_namespace?(ns)
|
80
84
|
[schema, *schema.includes.map(&:imported_schema)]
|
81
|
-
elsif (import = schema.import_by_namespace(
|
85
|
+
elsif (import = schema.import_by_namespace(ns))
|
82
86
|
[import.imported_schema]
|
83
87
|
else
|
84
|
-
raise Error, "Schema not found for namespace '#{
|
88
|
+
raise Error, "Schema not found for namespace '#{ns}' in '#{schema.id || schema.target_namespace}'"
|
85
89
|
end
|
86
90
|
end
|
87
91
|
|
@@ -96,9 +100,9 @@ module XSD
|
|
96
100
|
curname = curname.to_s
|
97
101
|
|
98
102
|
if curname[0] == '@'
|
99
|
-
result = result.collect_attributes.find { |attr| attr
|
103
|
+
result = result.collect_attributes.find { |attr| definition_match?(attr, curname[1..]) }
|
100
104
|
else
|
101
|
-
result = result.collect_elements.find { |elem| elem
|
105
|
+
result = result.collect_elements.find { |elem| definition_match?(elem, curname) }
|
102
106
|
end
|
103
107
|
end
|
104
108
|
|
@@ -112,7 +116,7 @@ module XSD
|
|
112
116
|
def object_by_name(node_name, name)
|
113
117
|
# get prefix and local name
|
114
118
|
name_prefix = get_prefix(name)
|
115
|
-
name_local
|
119
|
+
name_local = strip_prefix(name)
|
116
120
|
|
117
121
|
# do not search for built-in types
|
118
122
|
return nil if schema.namespace_prefix == name_prefix
|
@@ -122,7 +126,7 @@ module XSD
|
|
122
126
|
|
123
127
|
# find element in target schema
|
124
128
|
search_schemas.each do |s|
|
125
|
-
node = s.node.
|
129
|
+
node = s.node.at_xpath("./xs:#{node_name}[@name=\"#{name_local}\"]", { 'xs' => XML_SCHEMA })
|
126
130
|
return s.node_to_object(node) if node
|
127
131
|
end
|
128
132
|
|
@@ -134,7 +138,6 @@ module XSD
|
|
134
138
|
# @return BaseObject
|
135
139
|
def node_to_object(node)
|
136
140
|
# check object in cache first
|
137
|
-
# TODO: проверить работу!
|
138
141
|
return reader.object_cache[node.object_id] if reader.object_cache[node.object_id]
|
139
142
|
|
140
143
|
klass = XML::CLASS_MAP[node.name]
|
@@ -199,43 +202,49 @@ module XSD
|
|
199
202
|
# Get all available elements on the current stack level
|
200
203
|
# @return Array<Element>
|
201
204
|
def collect_elements(*)
|
202
|
-
|
203
|
-
return [] if NO_ELEMENTS_CONTAINER.include?(self.class.mapped_name)
|
205
|
+
return @collect_elements if @collect_elements
|
204
206
|
|
205
|
-
if
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
map_children(:*).map do |obj|
|
210
|
-
if obj.is_a?(Element)
|
211
|
-
obj
|
207
|
+
r = if NO_ELEMENTS_CONTAINER.include?(self.class.mapped_name)
|
208
|
+
[]
|
209
|
+
elsif is_a?(Referenced) && ref
|
210
|
+
reference.collect_elements
|
212
211
|
else
|
213
|
-
#
|
214
|
-
(
|
212
|
+
# map children recursive
|
213
|
+
map_children(:*).map do |obj|
|
214
|
+
if obj.is_a?(Element)
|
215
|
+
obj
|
216
|
+
else
|
217
|
+
# get elements considering references
|
218
|
+
(obj.is_a?(Referenced) && obj.ref ? obj.reference : obj).collect_elements
|
219
|
+
end
|
220
|
+
end.flatten
|
215
221
|
end
|
216
|
-
|
217
|
-
|
222
|
+
|
223
|
+
@collect_elements = r
|
218
224
|
end
|
219
225
|
|
220
226
|
# Get all available attributes on the current stack level
|
221
227
|
# @return Array<Attribute>
|
222
228
|
def collect_attributes(*)
|
223
|
-
|
224
|
-
return [] if NO_ATTRIBUTES_CONTAINER.include?(self.class.mapped_name)
|
229
|
+
return @collect_attributes if @collect_attributes
|
225
230
|
|
226
|
-
if
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
map_children(:*).map do |obj|
|
231
|
-
if obj.is_a?(Attribute)
|
232
|
-
obj
|
231
|
+
r = if NO_ATTRIBUTES_CONTAINER.include?(self.class.mapped_name)
|
232
|
+
[]
|
233
|
+
elsif is_a?(Referenced) && ref
|
234
|
+
reference.collect_attributes
|
233
235
|
else
|
234
|
-
#
|
235
|
-
(
|
236
|
+
# map children recursive
|
237
|
+
map_children(:*).map do |obj|
|
238
|
+
if obj.is_a?(Attribute)
|
239
|
+
obj
|
240
|
+
else
|
241
|
+
# get attributes considering references
|
242
|
+
(obj.is_a?(Referenced) && obj.ref ? obj.reference : obj).collect_attributes
|
243
|
+
end
|
244
|
+
end.flatten
|
236
245
|
end
|
237
|
-
|
238
|
-
|
246
|
+
|
247
|
+
@collect_attributes = r
|
239
248
|
end
|
240
249
|
|
241
250
|
# Get reader instance
|
@@ -256,8 +265,8 @@ module XSD
|
|
256
265
|
# @param [Hash] options
|
257
266
|
def self.property(name, type, options = {}, &block)
|
258
267
|
properties[to_underscore(name)] = {
|
259
|
-
name:
|
260
|
-
type:
|
268
|
+
name: name,
|
269
|
+
type: type,
|
261
270
|
resolve: block,
|
262
271
|
**options
|
263
272
|
}
|
@@ -294,7 +303,7 @@ module XSD
|
|
294
303
|
|
295
304
|
# check for property first
|
296
305
|
if (property = self.class.properties[method])
|
297
|
-
value
|
306
|
+
value = property[:resolve] ? property[:resolve].call(self) : node[property[:name].to_s]
|
298
307
|
result = if value.nil?
|
299
308
|
# if object has reference - search property in referenced object
|
300
309
|
node['ref'] ? reference.send(method, *args) : property[:default]
|
@@ -303,7 +312,7 @@ module XSD
|
|
303
312
|
when :integer
|
304
313
|
property[:name] == :maxOccurs && value == 'unbounded' ? :unbounded : value.to_i
|
305
314
|
when :boolean
|
306
|
-
|
315
|
+
value == 'true'
|
307
316
|
else
|
308
317
|
value
|
309
318
|
end
|
@@ -350,10 +359,10 @@ module XSD
|
|
350
359
|
def self.mapped_name
|
351
360
|
# @mapped_name ||= XML::CLASS_MAP.each { |k, v| return k.to_sym if v == self }
|
352
361
|
@mapped_name ||= begin
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
362
|
+
name = self.name.split('::').last
|
363
|
+
name[0] = name[0].downcase
|
364
|
+
name.to_sym
|
365
|
+
end
|
357
366
|
end
|
358
367
|
|
359
368
|
# Return string if it is not empty, or nil otherwise
|
@@ -362,5 +371,24 @@ module XSD
|
|
362
371
|
def nil_if_empty(string)
|
363
372
|
string&.empty? ? nil : string
|
364
373
|
end
|
374
|
+
|
375
|
+
private
|
376
|
+
|
377
|
+
def definition_match?(definition, query)
|
378
|
+
actual_definition = definition.referenced? ? definition.reference : definition
|
379
|
+
|
380
|
+
if query.start_with?('{')
|
381
|
+
parts = query[1..].split('}')
|
382
|
+
raise Error, "Invalid element/attribute query: #{query}" if parts.size != 2
|
383
|
+
|
384
|
+
namespace, name = parts
|
385
|
+
|
386
|
+
return false if namespace != actual_definition.namespace
|
387
|
+
else
|
388
|
+
name = query
|
389
|
+
end
|
390
|
+
|
391
|
+
name == '*' || actual_definition.name == name
|
392
|
+
end
|
365
393
|
end
|
366
394
|
end
|
@@ -10,10 +10,11 @@ module XSD
|
|
10
10
|
|
11
11
|
include SimpleTyped
|
12
12
|
include Referenced
|
13
|
+
include Named
|
13
14
|
|
14
15
|
# Optional. Specifies the name of the attribute. Name and ref attributes cannot both be present
|
15
16
|
# @!attribute name
|
16
|
-
# @return String
|
17
|
+
# @return String, nil
|
17
18
|
property :name, :string
|
18
19
|
|
19
20
|
# Optional. Specifies a default value for the attribute. Default and fixed attributes cannot both be present
|
@@ -34,7 +35,7 @@ module XSD
|
|
34
35
|
# matched against the (NCName) of the attribute
|
35
36
|
# @!attribute form
|
36
37
|
# @return String, nil
|
37
|
-
# TODO:
|
38
|
+
# TODO: support default value from parent
|
38
39
|
property :form, :string
|
39
40
|
|
40
41
|
# Optional. Specifies a built-in data type or a simple type. The type attribute can only be present when the
|
@@ -68,5 +69,15 @@ module XSD
|
|
68
69
|
def prohibited?
|
69
70
|
use == 'prohibited'
|
70
71
|
end
|
72
|
+
|
73
|
+
# Get base data type
|
74
|
+
# @return String, nil
|
75
|
+
def data_type
|
76
|
+
if simple_type
|
77
|
+
simple_type.data_type
|
78
|
+
else
|
79
|
+
strip_prefix(type)
|
80
|
+
end
|
81
|
+
end
|
71
82
|
end
|
72
83
|
end
|
@@ -9,10 +9,11 @@ module XSD
|
|
9
9
|
class AttributeGroup < BaseObject
|
10
10
|
include Referenced
|
11
11
|
include AttributeContainer
|
12
|
+
include Named
|
12
13
|
|
13
|
-
# Optional. Specifies the name of the attribute. Name and ref attributes cannot both be present
|
14
|
+
# Optional. Specifies the name of the attribute group. Name and ref attributes cannot both be present
|
14
15
|
# @!attribute name
|
15
|
-
# @return String
|
16
|
+
# @return String, nil
|
16
17
|
property :name, :string
|
17
18
|
end
|
18
19
|
end
|
@@ -7,8 +7,9 @@ module XSD
|
|
7
7
|
# https://www.w3schools.com/xml/el_complextype.asp
|
8
8
|
class ComplexType < BaseObject
|
9
9
|
include AttributeContainer
|
10
|
+
include Named
|
10
11
|
|
11
|
-
# Optional. Specifies
|
12
|
+
# Optional. Specifies a name for the element
|
12
13
|
# @!attribute name
|
13
14
|
# @return String
|
14
15
|
property :name, :string
|
@@ -78,5 +79,55 @@ module XSD
|
|
78
79
|
def linked?
|
79
80
|
!name.nil?
|
80
81
|
end
|
82
|
+
|
83
|
+
# Determine if this type has mixed content definition
|
84
|
+
# @return Boolean
|
85
|
+
def mixed_content?
|
86
|
+
return true if mixed
|
87
|
+
|
88
|
+
if complex_content
|
89
|
+
return true if complex_content.mixed
|
90
|
+
|
91
|
+
return (complex_content.extension || complex_content.restriction).base_complex_type&.mixed_content?
|
92
|
+
end
|
93
|
+
|
94
|
+
false
|
95
|
+
end
|
96
|
+
|
97
|
+
# Determine if this type has complex content definition
|
98
|
+
# @return Boolean
|
99
|
+
def complex_content?
|
100
|
+
if simple_content
|
101
|
+
false
|
102
|
+
elsif complex_content
|
103
|
+
true
|
104
|
+
else
|
105
|
+
group || all || choice || sequence
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Determine if this type has simple content definition
|
110
|
+
# @return Boolean
|
111
|
+
def simple_content?
|
112
|
+
!!simple_content
|
113
|
+
end
|
114
|
+
|
115
|
+
# Get simple content data type
|
116
|
+
# @return String, nil
|
117
|
+
def data_type
|
118
|
+
return nil unless simple_content
|
119
|
+
|
120
|
+
restriction = simple_content.restriction
|
121
|
+
if restriction
|
122
|
+
if restriction.base
|
123
|
+
restriction.base_simple_type&.data_type || strip_prefix(restriction.base)
|
124
|
+
else
|
125
|
+
restriction.simple_type&.data_type
|
126
|
+
end
|
127
|
+
else
|
128
|
+
extension = simple_content.extension
|
129
|
+
extension.base_simple_type&.data_type || strip_prefix(extension.base)
|
130
|
+
end
|
131
|
+
end
|
81
132
|
end
|
82
133
|
end
|
data/lib/xsd/objects/element.rb
CHANGED
@@ -11,10 +11,11 @@ module XSD
|
|
11
11
|
include SimpleTyped
|
12
12
|
include ComplexTyped
|
13
13
|
include Referenced
|
14
|
+
include Named
|
14
15
|
|
15
|
-
# Optional. Specifies
|
16
|
+
# Optional. Specifies a name for the element. This attribute is required if the parent element is the schema element
|
16
17
|
# @!attribute name
|
17
|
-
# @return String
|
18
|
+
# @return String, nil
|
18
19
|
property :name, :string
|
19
20
|
|
20
21
|
# Optional. Specifies either the name of a built-in data type, or the name of a simpleType or complexType element
|
@@ -107,8 +108,32 @@ module XSD
|
|
107
108
|
|
108
109
|
# Determine if element has complex content
|
109
110
|
# @return Boolean
|
110
|
-
def
|
111
|
-
|
111
|
+
def complex_content?
|
112
|
+
# this is not an opposite to simple_content?, element may have neither
|
113
|
+
complex_type&.complex_content? || false
|
114
|
+
end
|
115
|
+
|
116
|
+
# Determine if element has simple content
|
117
|
+
# @return Boolean
|
118
|
+
def simple_content?
|
119
|
+
# this is not an opposite to complex_content?, element may have neither
|
120
|
+
if complex_type
|
121
|
+
complex_type.simple_content?
|
122
|
+
else
|
123
|
+
true
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# Determine if element has attributes
|
128
|
+
# @return Boolean
|
129
|
+
def attributes?
|
130
|
+
complex_type && collect_attributes.any?
|
131
|
+
end
|
132
|
+
|
133
|
+
# Determine if element has mixed content
|
134
|
+
# @return Boolean
|
135
|
+
def mixed_content?
|
136
|
+
complex_type&.mixed_content? || false
|
112
137
|
end
|
113
138
|
|
114
139
|
# Get elements that can appear instead of this one
|
@@ -117,14 +142,20 @@ module XSD
|
|
117
142
|
# TODO: for now we do not search in parent schemas (that imported current schema)
|
118
143
|
# TODO: refactor for better namespace handling (use xpath with namespaces or correct comparison)
|
119
144
|
schema.collect_elements.select do |element|
|
120
|
-
element.substitution_group
|
145
|
+
strip_prefix(element.substitution_group) == name
|
121
146
|
end
|
122
147
|
end
|
123
148
|
|
124
|
-
# Get
|
125
|
-
# @return String
|
126
|
-
def
|
127
|
-
|
149
|
+
# Get base data type
|
150
|
+
# @return String, nil
|
151
|
+
def data_type
|
152
|
+
if complex_type
|
153
|
+
complex_type.data_type
|
154
|
+
elsif simple_type
|
155
|
+
simple_type.data_type
|
156
|
+
else
|
157
|
+
strip_prefix(type)
|
158
|
+
end
|
128
159
|
end
|
129
160
|
end
|
130
161
|
end
|
@@ -6,9 +6,12 @@ module XSD
|
|
6
6
|
# Parent elements: attribute, element, list, restriction, schema, union
|
7
7
|
# https://www.w3schools.com/xml/el_simpletype.asp
|
8
8
|
class SimpleType < BaseObject
|
9
|
-
|
9
|
+
include Named
|
10
|
+
|
11
|
+
# Specifies a name for the element. This attribute is required if the simpleType element is a child of the
|
12
|
+
# schema element, otherwise it is not allowed
|
10
13
|
# @!attribute name
|
11
|
-
# @return String
|
14
|
+
# @return String, nil
|
12
15
|
property :name, :string
|
13
16
|
|
14
17
|
# Nested restriction
|
@@ -31,5 +34,23 @@ module XSD
|
|
31
34
|
def linked?
|
32
35
|
!name.nil?
|
33
36
|
end
|
37
|
+
|
38
|
+
# Get base data type
|
39
|
+
# @return String, nil
|
40
|
+
def data_type
|
41
|
+
if restriction
|
42
|
+
if restriction.base
|
43
|
+
restriction.base_simple_type&.data_type || strip_prefix(restriction.base)
|
44
|
+
else
|
45
|
+
restriction.simple_type&.data_type
|
46
|
+
end
|
47
|
+
elsif union
|
48
|
+
types = union.types.map { |type| type.is_a?(String) ? strip_prefix(type) : type.data_type }.uniq
|
49
|
+
types.size == 1 ? types.first : types
|
50
|
+
else
|
51
|
+
# list is always a sting
|
52
|
+
'string'
|
53
|
+
end
|
54
|
+
end
|
34
55
|
end
|
35
56
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module XSD
|
4
|
+
module Named
|
5
|
+
# Get definition namespace
|
6
|
+
# @return String
|
7
|
+
def namespace
|
8
|
+
schema.target_namespace
|
9
|
+
end
|
10
|
+
|
11
|
+
# Get absolute definition name
|
12
|
+
# @return String
|
13
|
+
def absolute_name
|
14
|
+
name ? "{#{namespace}}#{name}" : nil
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/xsd/version.rb
CHANGED
data/lib/xsd/xml.rb
CHANGED
data/lib/xsd.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'xsd/base_object'
|
4
|
+
require 'xsd/shared/named'
|
4
5
|
require 'xsd/shared/min_max_occurs'
|
5
6
|
require 'xsd/shared/referenced'
|
6
7
|
require 'xsd/shared/attribute_container'
|
@@ -37,6 +38,5 @@ require 'xsd/objects/appinfo'
|
|
37
38
|
require 'xsd/objects/any_attribute'
|
38
39
|
require 'xsd/objects/key'
|
39
40
|
require 'xsd/objects/keyref'
|
40
|
-
require 'xsd/generator'
|
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.6.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: 2024-04-
|
11
|
+
date: 2024-04-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: builder
|
@@ -72,7 +72,6 @@ files:
|
|
72
72
|
- lib/xsd.rb
|
73
73
|
- lib/xsd/base_object.rb
|
74
74
|
- lib/xsd/exceptions.rb
|
75
|
-
- lib/xsd/generator.rb
|
76
75
|
- lib/xsd/objects/all.rb
|
77
76
|
- lib/xsd/objects/annotation.rb
|
78
77
|
- lib/xsd/objects/any.rb
|
@@ -107,6 +106,7 @@ files:
|
|
107
106
|
- lib/xsd/shared/complex_typed.rb
|
108
107
|
- lib/xsd/shared/element_container.rb
|
109
108
|
- lib/xsd/shared/min_max_occurs.rb
|
109
|
+
- lib/xsd/shared/named.rb
|
110
110
|
- lib/xsd/shared/referenced.rb
|
111
111
|
- lib/xsd/shared/simple_typed.rb
|
112
112
|
- lib/xsd/version.rb
|
data/lib/xsd/generator.rb
DELETED
@@ -1,146 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'builder'
|
4
|
-
|
5
|
-
module XSD
|
6
|
-
module Generator
|
7
|
-
# Generate XML from provided data
|
8
|
-
# @param [Hash] data
|
9
|
-
# @param [String, Array<String>] element
|
10
|
-
# @param [Builder::XmlMarkup] builder
|
11
|
-
# @return Builder::XmlMarkup
|
12
|
-
def generate(data, element = nil, builder = nil)
|
13
|
-
# find root element
|
14
|
-
root = find_root_element(element)
|
15
|
-
|
16
|
-
# create builder
|
17
|
-
builder ||= default_builder
|
18
|
-
|
19
|
-
# build element tree
|
20
|
-
@namespace_index = 0
|
21
|
-
build_element(builder, root, data)
|
22
|
-
|
23
|
-
builder
|
24
|
-
end
|
25
|
-
|
26
|
-
private
|
27
|
-
|
28
|
-
# Build element tree
|
29
|
-
# @param [Builder::XmlMarkup] xml
|
30
|
-
# @param [Element] element
|
31
|
-
# @param [Hash] data
|
32
|
-
# @param [Hash] namespaces
|
33
|
-
def build_element(xml, element, data, namespaces = {})
|
34
|
-
# get item data
|
35
|
-
elements = (element.abstract ? [] : [element]) + element.substitution_elements
|
36
|
-
provided_element = elements.find { |elem| !data[elem.name].nil? }
|
37
|
-
|
38
|
-
if element.required? && provided_element.nil?
|
39
|
-
raise Error, "Element #{element.name} is required, but no data in provided for it"
|
40
|
-
end
|
41
|
-
return unless provided_element
|
42
|
-
|
43
|
-
element = provided_element
|
44
|
-
data = data[element.name]
|
45
|
-
|
46
|
-
# handle repeated items
|
47
|
-
if element.multiple_allowed?
|
48
|
-
unless data.is_a?(Array)
|
49
|
-
raise Error, "Element #{element.name} is allowed to occur multiple times, but non-array is provided"
|
50
|
-
end
|
51
|
-
else
|
52
|
-
if data.is_a?(Array)
|
53
|
-
raise Error, "Element #{element.name} is not allowed to occur multiple times, but an array is provided"
|
54
|
-
end
|
55
|
-
|
56
|
-
data = [data]
|
57
|
-
end
|
58
|
-
|
59
|
-
# configure namespaces
|
60
|
-
# TODO: попытаться использовать collect_namespaces?
|
61
|
-
attributes = {}
|
62
|
-
all_attributes = element.collect_attributes
|
63
|
-
if element.complex?
|
64
|
-
all_elements = element.collect_elements
|
65
|
-
|
66
|
-
# get namespaces for current element and it's children
|
67
|
-
prefix = nil
|
68
|
-
[*all_elements, element].each do |elem|
|
69
|
-
prefix = get_namespace_prefix(elem, attributes, namespaces)
|
70
|
-
end
|
71
|
-
else
|
72
|
-
prefix = get_namespace_prefix(element, attributes, namespaces)
|
73
|
-
end
|
74
|
-
|
75
|
-
# iterate through each item
|
76
|
-
data.each do |item|
|
77
|
-
# prepare attributes
|
78
|
-
all_attributes.each do |attribute|
|
79
|
-
value = item["@#{attribute.name}"]
|
80
|
-
if !value.nil?
|
81
|
-
attributes[attribute.name] = value
|
82
|
-
elsif attribute.required?
|
83
|
-
raise Error, "Attribute #{attribute.name} is required, but no data in provided for it" if attribute.fixed.nil?
|
84
|
-
|
85
|
-
attributes[attribute.name] = attribute.fixed
|
86
|
-
else
|
87
|
-
attributes.delete(attribute.name)
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
# generate element
|
92
|
-
if element.complex?
|
93
|
-
# generate tag recursively
|
94
|
-
xml.tag!("#{prefix}:#{element.name}", attributes) do
|
95
|
-
all_elements.each do |elem|
|
96
|
-
build_element(xml, elem, item, namespaces.dup)
|
97
|
-
end
|
98
|
-
end
|
99
|
-
else
|
100
|
-
has_text = item.is_a?(Hash)
|
101
|
-
if has_text && element.complex_type&.nodes(:any, true)&.any?
|
102
|
-
xml.tag!("#{prefix}:#{element.name}", attributes) { |res| res << item['#text'].to_s }
|
103
|
-
else
|
104
|
-
value = has_text ? item['#text'] : item
|
105
|
-
xml.tag!("#{prefix}:#{element.name}", attributes, (value == '' ? nil : value))
|
106
|
-
end
|
107
|
-
end
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
# Get namespace prefix for element
|
112
|
-
# @param [Element] element
|
113
|
-
# @param [Hash] attributes
|
114
|
-
# @param [Hash] namespaces
|
115
|
-
# @return String
|
116
|
-
def get_namespace_prefix(element, attributes, namespaces)
|
117
|
-
namespace = (element.referenced? ? element.reference : element).target_namespace
|
118
|
-
prefix = namespaces.key(namespace)
|
119
|
-
unless prefix
|
120
|
-
prefix = "n#{@namespace_index += 1}"
|
121
|
-
namespaces[prefix] = attributes["xmlns:#{prefix}"] = namespace
|
122
|
-
end
|
123
|
-
|
124
|
-
prefix
|
125
|
-
end
|
126
|
-
|
127
|
-
# Find root element with provided lookup
|
128
|
-
# @param [String, Array<String>, nil] lookup
|
129
|
-
def find_root_element(lookup)
|
130
|
-
if lookup
|
131
|
-
element = self[*lookup]
|
132
|
-
raise Error, "Cant find start element #{lookup}" unless element.is_a?(Element)
|
133
|
-
|
134
|
-
element
|
135
|
-
else
|
136
|
-
raise Error, 'XSD contains more that one root element. Please, specify starting element' if elements.size > 1
|
137
|
-
|
138
|
-
elements.first
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
def default_builder
|
143
|
-
Builder::XmlMarkup.new
|
144
|
-
end
|
145
|
-
end
|
146
|
-
end
|