xsd 2.4.3 → 2.6.1

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: 07f0d2e114fa193dacc2ad0d9d74bbc173cbd3ebebcbae3d5a86152cc05abdc9
4
- data.tar.gz: f806f42ee894bd5a14704842d4e2f6da61559d006edce27e3dcb6d29568ab54b
3
+ metadata.gz: 73e87995baa341819126aaf56de8b4a865395423b6ea2f64c8a3aa8e702a611a
4
+ data.tar.gz: d330c3d390ff34c43d7988e0fe30b99d8538c62ff60205e0164d3119118ed1c4
5
5
  SHA512:
6
- metadata.gz: 5ecb651c68e56eac792410385234e8ef48a366d2534978885e2f89023080b6d5de54d6e3d5ad53fe30d6d6eee074a46a1095dec87549bb11a78dea9ef5787773
7
- data.tar.gz: 9d43d08a7281787b1d32f572339718620d1e88eff4f208b4d80fdb753086c4d996c7fb0d7b9277f5f00dcae9ff3bd13660d1207891e9c61265888575e48f7aad
6
+ metadata.gz: 0bfc579e3376d04a72c4ac593d9c944c7739b0dc4ffe0ea94399fbdfb11771bb8f7a4d356609a257e47e5ff3fefa7bae6c34a0ac0cb76c1b149614b25fe58994
7
+ data.tar.gz: 2d44c024e3b658e2b907614a621db0d2367feac05e6abeb8c5e7bee027d830c3be6f4a438941141e17b1df626f3b5899793a9e7befc612bd28caae804812cf7b
data/CHANGELOG.md CHANGED
@@ -1,4 +1,13 @@
1
- ## [Unreleased]
1
+ ## [2.6.1] - 2024-04-12
2
+
3
+ - Correctly detect namespace for referenced elements
4
+
5
+ ## [2.6.0] - 2024-04-12
6
+
7
+ - Fix XSD boolean attributes
8
+ - Add support for element lookup with namespace
9
+ - Add element (with simple content) data type calculation
10
+ - Add element mixed type calculation
2
11
 
3
12
  ## [2.4.3] - 2024-04-04
4
13
 
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
@@ -100,9 +100,9 @@ module XSD
100
100
  curname = curname.to_s
101
101
 
102
102
  if curname[0] == '@'
103
- result = result.collect_attributes.find { |attr| attr.name == curname[1..-1] }
103
+ result = result.collect_attributes.find { |attr| definition_match?(attr, curname[1..]) }
104
104
  else
105
- result = result.collect_elements.find { |elem| elem.name == curname }
105
+ result = result.collect_elements.find { |elem| definition_match?(elem, curname) }
106
106
  end
107
107
  end
108
108
 
@@ -116,7 +116,7 @@ module XSD
116
116
  def object_by_name(node_name, name)
117
117
  # get prefix and local name
118
118
  name_prefix = get_prefix(name)
119
- name_local = strip_prefix(name)
119
+ name_local = strip_prefix(name)
120
120
 
121
121
  # do not search for built-in types
122
122
  return nil if schema.namespace_prefix == name_prefix
@@ -126,7 +126,7 @@ module XSD
126
126
 
127
127
  # find element in target schema
128
128
  search_schemas.each do |s|
129
- 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 })
130
130
  return s.node_to_object(node) if node
131
131
  end
132
132
 
@@ -138,7 +138,6 @@ module XSD
138
138
  # @return BaseObject
139
139
  def node_to_object(node)
140
140
  # check object in cache first
141
- # TODO: проверить работу!
142
141
  return reader.object_cache[node.object_id] if reader.object_cache[node.object_id]
143
142
 
144
143
  klass = XML::CLASS_MAP[node.name]
@@ -203,43 +202,49 @@ module XSD
203
202
  # Get all available elements on the current stack level
204
203
  # @return Array<Element>
205
204
  def collect_elements(*)
206
- # exclude element that can not have elements
207
- return [] if NO_ELEMENTS_CONTAINER.include?(self.class.mapped_name)
205
+ return @collect_elements if @collect_elements
208
206
 
209
- if is_a?(Referenced) && ref
210
- reference.collect_elements
211
- else
212
- # map children recursive
213
- map_children(:*).map do |obj|
214
- if obj.is_a?(Element)
215
- obj
207
+ r = if NO_ELEMENTS_CONTAINER.include?(self.class.mapped_name)
208
+ []
209
+ elsif is_a?(Referenced) && ref
210
+ reference.collect_elements
216
211
  else
217
- # get elements considering references
218
- (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
219
221
  end
220
- end.flatten
221
- end
222
+
223
+ @collect_elements = r
222
224
  end
223
225
 
224
226
  # Get all available attributes on the current stack level
225
227
  # @return Array<Attribute>
226
228
  def collect_attributes(*)
227
- # exclude element that can not have elements
228
- return [] if NO_ATTRIBUTES_CONTAINER.include?(self.class.mapped_name)
229
+ return @collect_attributes if @collect_attributes
229
230
 
230
- if is_a?(Referenced) && ref
231
- reference.collect_attributes
232
- else
233
- # map children recursive
234
- map_children(:*).map do |obj|
235
- if obj.is_a?(Attribute)
236
- obj
231
+ r = if NO_ATTRIBUTES_CONTAINER.include?(self.class.mapped_name)
232
+ []
233
+ elsif is_a?(Referenced) && ref
234
+ reference.collect_attributes
237
235
  else
238
- # get attributes considering references
239
- (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
240
245
  end
241
- end.flatten
242
- end
246
+
247
+ @collect_attributes = r
243
248
  end
244
249
 
245
250
  # Get reader instance
@@ -260,8 +265,8 @@ module XSD
260
265
  # @param [Hash] options
261
266
  def self.property(name, type, options = {}, &block)
262
267
  properties[to_underscore(name)] = {
263
- name: name,
264
- type: type,
268
+ name: name,
269
+ type: type,
265
270
  resolve: block,
266
271
  **options
267
272
  }
@@ -298,7 +303,7 @@ module XSD
298
303
 
299
304
  # check for property first
300
305
  if (property = self.class.properties[method])
301
- 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]
302
307
  result = if value.nil?
303
308
  # if object has reference - search property in referenced object
304
309
  node['ref'] ? reference.send(method, *args) : property[:default]
@@ -307,7 +312,7 @@ module XSD
307
312
  when :integer
308
313
  property[:name] == :maxOccurs && value == 'unbounded' ? :unbounded : value.to_i
309
314
  when :boolean
310
- !!value
315
+ value == 'true'
311
316
  else
312
317
  value
313
318
  end
@@ -354,10 +359,10 @@ module XSD
354
359
  def self.mapped_name
355
360
  # @mapped_name ||= XML::CLASS_MAP.each { |k, v| return k.to_sym if v == self }
356
361
  @mapped_name ||= begin
357
- name = self.name.split('::').last
358
- name[0] = name[0].downcase
359
- name.to_sym
360
- end
362
+ name = self.name.split('::').last
363
+ name[0] = name[0].downcase
364
+ name.to_sym
365
+ end
361
366
  end
362
367
 
363
368
  # Return string if it is not empty, or nil otherwise
@@ -366,5 +371,23 @@ module XSD
366
371
  def nil_if_empty(string)
367
372
  string&.empty? ? nil : string
368
373
  end
374
+
375
+ private
376
+
377
+ def definition_match?(definition, query)
378
+ # namespace included in query
379
+ if query.start_with?('{')
380
+ parts = query[1..].split('}')
381
+ raise Error, "Invalid element/attribute query: #{query}" if parts.size != 2
382
+
383
+ namespace, name = parts
384
+
385
+ return false if namespace != definition.namespace
386
+ else
387
+ name = query
388
+ end
389
+
390
+ name == '*' || definition.name == name
391
+ end
369
392
  end
370
393
  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
@@ -8,8 +8,9 @@ module XSD
8
8
  class Group < BaseObject
9
9
  include MinMaxOccurs
10
10
  include Referenced
11
+ include Named
11
12
 
12
- # Optional. Specifies the name of the attribute. Name and ref attributes cannot both be present
13
+ # Optional. Specifies a name for the group. This attribute is used only when the schema element is the parent of this group element. Name and ref attributes cannot both be present
13
14
  # @!attribute name
14
15
  # @return String
15
16
  property :name, :string
@@ -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,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module XSD
4
+ module Named
5
+ # Get definition namespace
6
+ # @return String
7
+ def namespace
8
+ is_referenced = respond_to?(:referenced?)
9
+ is_referenced && referenced? ? reference.schema.target_namespace : schema.target_namespace
10
+ end
11
+
12
+ # Get absolute definition name
13
+ # @return String
14
+ def absolute_name
15
+ name ? "{#{namespace}}#{name}" : nil
16
+ end
17
+ end
18
+ 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.3'
4
+ VERSION = '2.6.1'
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.3
4
+ version: 2.6.1
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