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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5866e8be4b81ee97f2df8d4b1df3f585d94a3edcb7408ef45d6f8fee2fb89f1e
4
- data.tar.gz: 793e41c9424ca2cce3d0b9f5b26b38d9e39696c31c29910ba3f9223dd6850724
3
+ metadata.gz: 2a5aa03e831e5cf4e88780b18a4ef57217eff54a14e2138669672f33486ba8bd
4
+ data.tar.gz: b6a9ab392e47192a6a286ffa7dc97c5f624c78d9ab117bdea67e11f801c39363
5
5
  SHA512:
6
- metadata.gz: 89bee39c770bb75941940911c875159b455443a97fdb10d6bb0064a3acfb557d66f106fba45701940aff51f07f2f61a5d7f44453ed7b80a81426042da76dec8e
7
- data.tar.gz: 8b3f4004b78f7a5be63903514a60971a21b5107e4488882d2d2a36e260d6d6eaa6b200ab37491fe4060d50f8f00c18b0ced84aac3323819af41d02535fb7512a
6
+ metadata.gz: a031bc3a3947708a8f6f8e4add4040853f9f8b62cd3465de56421600d9fc2d2b7c45b163efe239c7486dce05bf0427d8f380e119673b644739f736b4598b5276
7
+ data.tar.gz: ad7bbd0268373292a291935599749dc9937784438b398958acf592db4e1648967106f45c1dd402c82f3cdd534f2fe0e177a1250e91f65872fc3aa9d9a8b416a5
data/CHANGELOG.md CHANGED
@@ -1,4 +1,14 @@
1
- ## [Unreleased]
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 elements and their child elements
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.complex? # => true
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
  ```
@@ -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 schema.targets_namespace?(ns_or_prefix)
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(ns_or_prefix))
85
+ elsif (import = schema.import_by_namespace(ns))
82
86
  [import.imported_schema]
83
87
  else
84
- raise Error, "Schema not found for namespace '#{ns_or_prefix}' in '#{schema.id || schema.target_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.name == curname[1..-1] }
103
+ result = result.collect_attributes.find { |attr| definition_match?(attr, curname[1..]) }
100
104
  else
101
- result = result.collect_elements.find { |elem| elem.name == curname }
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 = strip_prefix(name)
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.xpath("./xs:#{node_name}[@name=\"#{name_local}\"]", { 'xs' => XML_SCHEMA }).first
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
- # exclude element that can not have elements
203
- return [] if NO_ELEMENTS_CONTAINER.include?(self.class.mapped_name)
205
+ return @collect_elements if @collect_elements
204
206
 
205
- if is_a?(Referenced) && ref
206
- reference.collect_elements
207
- else
208
- # map children recursive
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
- # get elements considering references
214
- (obj.is_a?(Referenced) && obj.ref ? obj.reference : obj).collect_elements
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
- end.flatten
217
- end
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
- # exclude element that can not have elements
224
- return [] if NO_ATTRIBUTES_CONTAINER.include?(self.class.mapped_name)
229
+ return @collect_attributes if @collect_attributes
225
230
 
226
- if is_a?(Referenced) && ref
227
- reference.collect_attributes
228
- else
229
- # map children recursive
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
- # get attributes considering references
235
- (obj.is_a?(Referenced) && obj.ref ? obj.reference : obj).collect_attributes
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
- end.flatten
238
- end
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: name,
260
- type: 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 = property[:resolve] ? property[:resolve].call(self) : node[property[:name].to_s]
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
- !!value
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
- name = self.name.split('::').last
354
- name[0] = name[0].downcase
355
- name.to_sym
356
- end
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: поддержка default значения с вычислением родителя
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 the name of the attribute. Name and ref attributes cannot both be present
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
@@ -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 the name of the attribute. Name and ref attributes cannot both be present
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 complex?
111
- complex_type && !complex_type.simple_content && collect_elements.any?
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&.split(':')&.last == name
145
+ strip_prefix(element.substitution_group) == name
121
146
  end
122
147
  end
123
148
 
124
- # Get target namespace
125
- # @return String
126
- def target_namespace
127
- schema.target_namespace
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
- # Optional. Specifies the name of the attribute. Name and ref attributes cannot both be present
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module XSD
4
- VERSION = '2.4.2'
4
+ VERSION = '2.6.0'
5
5
  end
data/lib/xsd/xml.rb CHANGED
@@ -5,8 +5,6 @@ require 'net/http'
5
5
 
6
6
  module XSD
7
7
  class XML
8
- include Generator
9
-
10
8
  attr_reader :options, :object_cache, :schemas
11
9
 
12
10
  DEFAULT_RESOURCE_RESOLVER = proc do |location, namespace|
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.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-04 00:00:00.000000000 Z
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