lutaml-model 0.8.5 → 0.8.6
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/.github/workflows/dependent-tests.yml +4 -1
- data/.rubocop_todo.yml +97 -22
- data/docs/_migrations/0-8-0-namespace-restructuring.adoc +90 -0
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/xml/adapter/adapter_helpers.rb +1 -42
- data/lib/lutaml/xml/adapter/base_adapter.rb +48 -458
- data/lib/lutaml/xml/adapter/namespace_data.rb +0 -17
- data/lib/lutaml/xml/adapter/namespace_uri_collector.rb +71 -0
- data/lib/lutaml/xml/adapter/nokogiri_adapter.rb +5 -1110
- data/lib/lutaml/xml/adapter/oga_adapter.rb +6 -846
- data/lib/lutaml/xml/adapter/ox_adapter.rb +7 -884
- data/lib/lutaml/xml/adapter/plan_based_builder.rb +929 -0
- data/lib/lutaml/xml/adapter/rexml_adapter.rb +10 -864
- data/lib/lutaml/xml/adapter/xml_parser.rb +86 -0
- data/lib/lutaml/xml/adapter/xml_serializer.rb +291 -0
- data/lib/lutaml/xml/adapter.rb +0 -1
- data/lib/lutaml/xml/adapter_element.rb +7 -1
- data/lib/lutaml/xml/builder/base.rb +0 -1
- data/lib/lutaml/xml/data_model.rb +9 -1
- data/lib/lutaml/xml/document.rb +3 -1
- data/lib/lutaml/xml/element.rb +13 -10
- data/lib/lutaml/xml/serialization/format_conversion.rb +19 -42
- data/lib/lutaml/xml/serialization/instance_methods.rb +26 -35
- data/lib/lutaml/xml/transformation/custom_method_wrapper.rb +34 -55
- data/lib/lutaml/xml/transformation/rule_applier.rb +1 -1
- data/lib/lutaml/xml/xml_element.rb +24 -20
- data/spec/lutaml/xml/adapter/base_adapter_regression_spec.rb +151 -0
- data/spec/lutaml/xml/adapter/order_spec.rb +150 -0
- data/spec/lutaml/xml/clear_parse_state_spec.rb +139 -0
- data/spec/lutaml/xml/doubly_defined_namespace_spec.rb +0 -2
- data/spec/lutaml/xml/schema/compiler_spec.rb +75 -69
- data/spec/lutaml/xml/transformation/custom_method_wrapper_spec.rb +213 -14
- metadata +9 -3
- data/lib/lutaml/xml/adapter/xml_serialization.rb +0 -145
|
@@ -1,875 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require "rexml/document"
|
|
2
4
|
require "moxml"
|
|
3
5
|
require "moxml/adapter/rexml"
|
|
6
|
+
require_relative "base_adapter"
|
|
4
7
|
|
|
5
8
|
module Lutaml
|
|
6
9
|
module Xml
|
|
7
10
|
module Adapter
|
|
8
11
|
class RexmlAdapter < BaseAdapter
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
parsed = Moxml::Adapter::Rexml.parse(xml, encoding: parse_encoding)
|
|
18
|
-
root_element = parsed.root
|
|
19
|
-
|
|
20
|
-
if root_element.nil?
|
|
21
|
-
raise REXML::ParseException.new(
|
|
22
|
-
"Malformed XML: Unable to parse the provided XML document. " \
|
|
23
|
-
"The document structure is invalid or incomplete.",
|
|
24
|
-
)
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
@root = Rexml::Element.new(root_element)
|
|
28
|
-
@root.processing_instructions = extract_document_processing_instructions(parsed)
|
|
29
|
-
new(@root, parse_encoding)
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def to_xml(options = {})
|
|
33
|
-
encoding = determine_encoding(options)
|
|
34
|
-
builder_options = encoding ? { encoding: encoding } : {}
|
|
35
|
-
|
|
36
|
-
builder = Builder::Rexml.build(builder_options) do |xml|
|
|
37
|
-
if @root.is_a?(Rexml::Element)
|
|
38
|
-
# Case A: Old parsed XML (from Rexml::Element) - use build_xml
|
|
39
|
-
@root.build_xml(xml)
|
|
40
|
-
else
|
|
41
|
-
# Cases B & C: XmlElement or Model instance
|
|
42
|
-
# ARCHITECTURE: Normalize to XmlElement, then use single rendering path
|
|
43
|
-
|
|
44
|
-
# Determine the source (XmlElement or model instance)
|
|
45
|
-
original_model = nil
|
|
46
|
-
|
|
47
|
-
xml_element = if @root.is_a?(Lutaml::Xml::DataModel::XmlElement)
|
|
48
|
-
# Case B: Already an XmlElement
|
|
49
|
-
@root
|
|
50
|
-
else
|
|
51
|
-
# Case C: Model instance - check for custom methods first
|
|
52
|
-
mapper_class = options[:mapper_class] || @root.class
|
|
53
|
-
xml_mapping = mapper_class.mappings_for(:xml)
|
|
54
|
-
|
|
55
|
-
# Check if model has map_all with custom methods
|
|
56
|
-
# Custom methods work with model instances, not XmlElement trees
|
|
57
|
-
has_custom_map_all = xml_mapping.raw_mapping&.custom_methods &&
|
|
58
|
-
xml_mapping.raw_mapping.custom_methods[:to]
|
|
59
|
-
|
|
60
|
-
if has_custom_map_all
|
|
61
|
-
# Use legacy path for custom methods - don't transform
|
|
62
|
-
nil
|
|
63
|
-
else
|
|
64
|
-
# Transform model to XmlElement tree
|
|
65
|
-
original_model = @root
|
|
66
|
-
transformation = mapper_class.transformation_for(
|
|
67
|
-
:xml, register
|
|
68
|
-
)
|
|
69
|
-
transformation.transform(@root, options)
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
if xml_element
|
|
74
|
-
# Modern path: Use XmlElement + DeclarationPlan tree
|
|
75
|
-
mapper_class = options[:mapper_class] || xml_element.class
|
|
76
|
-
mapping = mapper_class.mappings_for(:xml)
|
|
77
|
-
|
|
78
|
-
# Phase 1: Collect namespace needs from XmlElement tree
|
|
79
|
-
collector = NamespaceCollector.new(register)
|
|
80
|
-
needs = collector.collect(xml_element, mapping,
|
|
81
|
-
mapper_class: mapper_class)
|
|
82
|
-
|
|
83
|
-
# Phase 2: Plan namespace declarations (builds ElementNode tree)
|
|
84
|
-
planner = DeclarationPlanner.new(register)
|
|
85
|
-
plan = planner.plan(xml_element, mapping, needs,
|
|
86
|
-
options: options)
|
|
87
|
-
|
|
88
|
-
# Phase 3: Render using XmlElement + DeclarationPlan
|
|
89
|
-
render_options = options.merge(is_root_element: true)
|
|
90
|
-
if original_model
|
|
91
|
-
render_options[:original_model] =
|
|
92
|
-
original_model
|
|
93
|
-
end
|
|
94
|
-
build_xml_element_with_plan(xml, xml_element, plan,
|
|
95
|
-
render_options)
|
|
96
|
-
else
|
|
97
|
-
# Legacy path: Model instance with custom methods
|
|
98
|
-
mapper_class = options[:mapper_class] || @root.class
|
|
99
|
-
xml_mapping = mapper_class.mappings_for(:xml)
|
|
100
|
-
|
|
101
|
-
collector = NamespaceCollector.new(register)
|
|
102
|
-
needs = collector.collect(@root, xml_mapping)
|
|
103
|
-
|
|
104
|
-
planner = DeclarationPlanner.new(register)
|
|
105
|
-
plan = planner.plan(@root, xml_mapping, needs, options: options)
|
|
106
|
-
|
|
107
|
-
build_element_with_plan(xml, @root, plan, options)
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
xml_data = builder.to_xml
|
|
113
|
-
options[:declaration] ? declaration(options) + xml_data : xml_data
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
# Build XML from XmlDataModel::XmlElement using DeclarationPlan tree (PARALLEL TRAVERSAL)
|
|
117
|
-
#
|
|
118
|
-
# @param xml [Builder::Rexml] XML builder
|
|
119
|
-
# @param xml_element [XmlDataModel::XmlElement] Element content
|
|
120
|
-
# @param plan [DeclarationPlan] Declaration plan with tree structure
|
|
121
|
-
# @param options [Hash] Serialization options
|
|
122
|
-
def build_xml_element_with_plan(xml, xml_element, plan, _options = {})
|
|
123
|
-
# Add processing instructions before root element
|
|
124
|
-
xml_element.processing_instructions.each do |pi|
|
|
125
|
-
xml.add_processing_instruction(pi.target, pi.content)
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
build_rexml_element(xml, xml_element, plan.root_node,
|
|
129
|
-
plan.global_prefix_registry, plan)
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
private
|
|
133
|
-
|
|
134
|
-
# Recursively build REXML elements (PARALLEL TRAVERSAL)
|
|
135
|
-
#
|
|
136
|
-
# @param xml [Builder::Rexml] XML builder
|
|
137
|
-
# @param xml_element [XmlDataModel::XmlElement] Content
|
|
138
|
-
# @param element_node [ElementNode] Decisions
|
|
139
|
-
# @param global_registry [Hash] Global prefix registry (URI => prefix)
|
|
140
|
-
# @return [void]
|
|
141
|
-
def build_rexml_element(xml, xml_element, element_node,
|
|
142
|
-
global_registry, plan)
|
|
143
|
-
qualified_name = element_node.qualified_name
|
|
144
|
-
|
|
145
|
-
# 1. Collect attributes (xmlns declarations + regular attributes)
|
|
146
|
-
attributes = {}
|
|
147
|
-
|
|
148
|
-
# 2. Add hoisted xmlns declarations
|
|
149
|
-
original_ns_uris = plan&.original_namespace_uris || {}
|
|
150
|
-
element_node.hoisted_declarations.each do |key, uri|
|
|
151
|
-
next if uri == "http://www.w3.org/XML/1998/namespace"
|
|
152
|
-
|
|
153
|
-
# Convert FPI to URN if necessary (REXML requires valid URI)
|
|
154
|
-
effective_uri = if self.class.fpi?(uri)
|
|
155
|
-
self.class.fpi_to_urn(uri)
|
|
156
|
-
else
|
|
157
|
-
original_ns_uris[uri] || uri
|
|
158
|
-
end
|
|
159
|
-
xmlns_name = key ? "xmlns:#{key}" : "xmlns"
|
|
160
|
-
attributes[xmlns_name] = effective_uri
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
# 3. Add regular attributes by INDEX (PARALLEL TRAVERSAL)
|
|
164
|
-
xml_element.attributes.each_with_index do |xml_attr, idx|
|
|
165
|
-
attr_node = element_node.attribute_nodes[idx]
|
|
166
|
-
attributes[attr_node.qualified_name] = xml_attr.value.to_s
|
|
167
|
-
end
|
|
168
|
-
|
|
169
|
-
# 4. Add xsi:nil if needed
|
|
170
|
-
if xml_element.respond_to?(:xsi_nil) && xml_element.xsi_nil
|
|
171
|
-
attributes["xsi:nil"] = "true"
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
# Add schema_location attribute from ElementNode if present
|
|
175
|
-
attributes.merge!(element_node.schema_location_attr) if element_node.schema_location_attr
|
|
176
|
-
|
|
177
|
-
# 5. Add xmlns="" if element needs to opt out of parent's default namespace
|
|
178
|
-
if element_node.needs_xmlns_blank
|
|
179
|
-
attributes["xmlns"] = ""
|
|
180
|
-
end
|
|
181
|
-
|
|
182
|
-
# 6. Create element with qualified name and attributes
|
|
183
|
-
xml.create_and_add_element(qualified_name,
|
|
184
|
-
attributes: attributes) do |inner_xml|
|
|
185
|
-
# 7. Handle raw content (map_all directive)
|
|
186
|
-
if xml_element.respond_to?(:raw_content)
|
|
187
|
-
raw_content = xml_element.raw_content
|
|
188
|
-
if raw_content && !raw_content.to_s.empty?
|
|
189
|
-
inner_xml.text(raw_content.to_s)
|
|
190
|
-
return
|
|
191
|
-
end
|
|
192
|
-
end
|
|
193
|
-
|
|
194
|
-
# 8. Add text content if present
|
|
195
|
-
if xml_element.text_content
|
|
196
|
-
if xml_element.cdata
|
|
197
|
-
inner_xml.cdata(xml_element.text_content.to_s)
|
|
198
|
-
else
|
|
199
|
-
inner_xml.text(xml_element.text_content.to_s)
|
|
200
|
-
end
|
|
201
|
-
end
|
|
202
|
-
|
|
203
|
-
# 9. Recursively build children by INDEX (PARALLEL TRAVERSAL)
|
|
204
|
-
child_element_index = 0
|
|
205
|
-
xml_element.children.each do |xml_child|
|
|
206
|
-
case xml_child
|
|
207
|
-
when Lutaml::Xml::DataModel::XmlElement
|
|
208
|
-
child_node = element_node.element_nodes[child_element_index]
|
|
209
|
-
child_element_index += 1
|
|
210
|
-
|
|
211
|
-
build_rexml_element(inner_xml, xml_child, child_node,
|
|
212
|
-
global_registry, plan)
|
|
213
|
-
when String
|
|
214
|
-
inner_xml.text(xml_child)
|
|
215
|
-
when ::Lutaml::Xml::DataModel::XmlComment
|
|
216
|
-
inner_xml.comment(xml_child.content)
|
|
217
|
-
end
|
|
218
|
-
end
|
|
219
|
-
end
|
|
220
|
-
end
|
|
221
|
-
|
|
222
|
-
public
|
|
223
|
-
|
|
224
|
-
def attributes_hash(element)
|
|
225
|
-
result = Lutaml::Model::MappingHash.new
|
|
226
|
-
|
|
227
|
-
element.attributes.each_value do |attr|
|
|
228
|
-
if attr.name == "schemaLocation"
|
|
229
|
-
result["__schema_location"] = {
|
|
230
|
-
namespace: attr.namespace,
|
|
231
|
-
prefix: attr.namespace.prefix,
|
|
232
|
-
schema_location: attr.value,
|
|
233
|
-
}
|
|
234
|
-
else
|
|
235
|
-
result[self.class.namespaced_attr_name(attr)] = attr.value
|
|
236
|
-
end
|
|
237
|
-
end
|
|
238
|
-
|
|
239
|
-
result
|
|
240
|
-
end
|
|
241
|
-
|
|
242
|
-
# NOTE: name_of, prefixed_name_of, namespaced_attr_name, namespaced_name_of
|
|
243
|
-
# are provided by AdapterHelpers module via extend
|
|
244
|
-
|
|
245
|
-
def self.text_of(element)
|
|
246
|
-
element.content
|
|
247
|
-
end
|
|
248
|
-
|
|
249
|
-
def order
|
|
250
|
-
children.filter_map do |child|
|
|
251
|
-
if child.text?
|
|
252
|
-
next if child.text.nil?
|
|
253
|
-
|
|
254
|
-
Element.new("Text", child.unprefixed_name)
|
|
255
|
-
elsif child.comment?
|
|
256
|
-
Element.new("Comment", "comment",
|
|
257
|
-
text_content: child.content,
|
|
258
|
-
node_type: :comment)
|
|
259
|
-
else
|
|
260
|
-
Element.new("Element", child.unprefixed_name)
|
|
261
|
-
end
|
|
262
|
-
end
|
|
263
|
-
end
|
|
264
|
-
|
|
265
|
-
def self.order_of(element)
|
|
266
|
-
element.children.map do |child|
|
|
267
|
-
instance_args = if TEXT_CLASSES.include?(child.class)
|
|
268
|
-
["Text", "text"]
|
|
269
|
-
else
|
|
270
|
-
["Element", name_of(child)]
|
|
271
|
-
end
|
|
272
|
-
Element.new(*instance_args)
|
|
273
|
-
end
|
|
274
|
-
end
|
|
275
|
-
|
|
276
|
-
def self.normalize_xml_for_rexml(xml)
|
|
277
|
-
return xml unless xml.is_a?(String) && xml.encoding.to_s != "UTF-8"
|
|
278
|
-
|
|
279
|
-
xml.encode("UTF-8")
|
|
280
|
-
end
|
|
281
|
-
|
|
282
|
-
def build_element_with_plan(xml, element, plan, options = {})
|
|
283
|
-
mapper_class = options[:mapper_class] || element.class
|
|
284
|
-
xml_mapping = mapper_class.mappings_for(:xml)
|
|
285
|
-
return xml unless xml_mapping
|
|
286
|
-
|
|
287
|
-
plan ||= {
|
|
288
|
-
namespaces: {},
|
|
289
|
-
children_plans: {},
|
|
290
|
-
type_namespaces: {},
|
|
291
|
-
}
|
|
292
|
-
# TYPE-ONLY MODELS: No element wrapper, serialize children directly
|
|
293
|
-
# BUT if we have a tag_name in options, that means parent wants a wrapper
|
|
294
|
-
if xml_mapping.no_element?
|
|
295
|
-
# If parent provided a tag_name, create that wrapper first
|
|
296
|
-
if options[:tag_name]
|
|
297
|
-
xml.create_and_add_element(options[:tag_name]) do |inner_xml|
|
|
298
|
-
# Serialize type-only model's children inside parent's wrapper
|
|
299
|
-
xml_mapping.elements.each do |element_rule|
|
|
300
|
-
next if options[:except]&.include?(element_rule.to)
|
|
301
|
-
|
|
302
|
-
attribute_def = mapper_class.attributes[element_rule.to]
|
|
303
|
-
next unless attribute_def
|
|
304
|
-
|
|
305
|
-
value = element.send(element_rule.to)
|
|
306
|
-
next unless element_rule.render?(value, element)
|
|
307
|
-
|
|
308
|
-
# For type-only models, children plans may not be available
|
|
309
|
-
# Serialize children directly
|
|
310
|
-
if value && attribute_def.type(register)&.<=(Lutaml::Model::Serialize)
|
|
311
|
-
# Nested model - recursively build it
|
|
312
|
-
child_plan = child_plan_for(plan, element_rule.to) || {
|
|
313
|
-
namespaces: {},
|
|
314
|
-
children_plans: {},
|
|
315
|
-
type_namespaces: {},
|
|
316
|
-
}
|
|
317
|
-
build_element_with_plan(
|
|
318
|
-
inner_xml,
|
|
319
|
-
value,
|
|
320
|
-
child_plan,
|
|
321
|
-
{ mapper_class: attribute_def.type(register),
|
|
322
|
-
tag_name: element_rule.name },
|
|
323
|
-
)
|
|
324
|
-
else
|
|
325
|
-
# Simple value - create element directly
|
|
326
|
-
inner_xml.create_and_add_element(element_rule.name) do
|
|
327
|
-
add_value(inner_xml, value, attribute_def,
|
|
328
|
-
cdata: element_rule.cdata)
|
|
329
|
-
end
|
|
330
|
-
end
|
|
331
|
-
end
|
|
332
|
-
end
|
|
333
|
-
else
|
|
334
|
-
# No wrapper at all - serialize children directly (for root-level type-only)
|
|
335
|
-
xml_mapping.elements.each do |element_rule|
|
|
336
|
-
next if options[:except]&.include?(element_rule.to)
|
|
337
|
-
|
|
338
|
-
attribute_def = mapper_class.attributes[element_rule.to]
|
|
339
|
-
next unless attribute_def
|
|
340
|
-
|
|
341
|
-
value = element.send(element_rule.to)
|
|
342
|
-
next unless element_rule.render?(value, element)
|
|
343
|
-
|
|
344
|
-
child_plan = child_plan_for(plan, element_rule.to)
|
|
345
|
-
|
|
346
|
-
if value && attribute_def.type(register)&.<=(Lutaml::Model::Serialize)
|
|
347
|
-
handle_nested_elements_with_plan(
|
|
348
|
-
xml,
|
|
349
|
-
value,
|
|
350
|
-
element_rule,
|
|
351
|
-
attribute_def,
|
|
352
|
-
child_plan,
|
|
353
|
-
options,
|
|
354
|
-
)
|
|
355
|
-
else
|
|
356
|
-
add_simple_value(xml, element_rule, value, attribute_def,
|
|
357
|
-
plan: plan, mapping: xml_mapping)
|
|
358
|
-
end
|
|
359
|
-
end
|
|
360
|
-
end
|
|
361
|
-
return xml
|
|
362
|
-
end
|
|
363
|
-
|
|
364
|
-
# Use xmlns declarations from plan
|
|
365
|
-
attributes = {}
|
|
366
|
-
|
|
367
|
-
# Apply namespace declarations from plan
|
|
368
|
-
plan[:namespaces]&.each_value do |ns_config|
|
|
369
|
-
next unless ns_config[:declared_at] == :here
|
|
370
|
-
|
|
371
|
-
ns_class = ns_config[:ns_object]
|
|
372
|
-
|
|
373
|
-
# Parse the ready-to-use declaration string
|
|
374
|
-
decl = ns_config[:xmlns_declaration]
|
|
375
|
-
if decl.start_with?("xmlns:")
|
|
376
|
-
# Prefixed namespace: "xmlns:prefix=\"uri\""
|
|
377
|
-
prefix = decl[/xmlns:(\w+)=/, 1]
|
|
378
|
-
attributes["xmlns:#{prefix}"] = ns_class.uri
|
|
379
|
-
else
|
|
380
|
-
# Default namespace: "xmlns=\"uri\""
|
|
381
|
-
attributes["xmlns"] = ns_class.uri
|
|
382
|
-
end
|
|
383
|
-
end
|
|
384
|
-
|
|
385
|
-
# Collect attribute custom methods to call after element creation
|
|
386
|
-
attribute_custom_methods = []
|
|
387
|
-
|
|
388
|
-
# Add regular attributes (non-xmlns)
|
|
389
|
-
xml_mapping.attributes.each do |attribute_rule|
|
|
390
|
-
next if options[:except]&.include?(attribute_rule.to)
|
|
391
|
-
|
|
392
|
-
# Collect custom methods for later execution (after element is created)
|
|
393
|
-
if attribute_rule.custom_methods[:to]
|
|
394
|
-
attribute_custom_methods << attribute_rule
|
|
395
|
-
next
|
|
396
|
-
end
|
|
397
|
-
|
|
398
|
-
mapping_rule_name = if attribute_rule.multiple_mappings?
|
|
399
|
-
attribute_rule.name.first
|
|
400
|
-
else
|
|
401
|
-
attribute_rule.name
|
|
402
|
-
end
|
|
403
|
-
|
|
404
|
-
attr = attribute_definition_for(element, attribute_rule,
|
|
405
|
-
mapper_class: mapper_class)
|
|
406
|
-
value = attribute_rule.to_value_for(element)
|
|
407
|
-
value = attr.serialize(value, :xml, register) if attr
|
|
408
|
-
value = ExportTransformer.call(value, attribute_rule, attr,
|
|
409
|
-
format: :xml)
|
|
410
|
-
value = value&.join(attribute_rule.delimiter) if attribute_rule.delimiter
|
|
411
|
-
|
|
412
|
-
if attribute_rule.as_list && attribute_rule.as_list[:export]
|
|
413
|
-
value = attribute_rule.as_list[:export].call(value)
|
|
414
|
-
end
|
|
415
|
-
|
|
416
|
-
if render_element?(attribute_rule, element, value)
|
|
417
|
-
# Resolve attribute namespace from plan
|
|
418
|
-
ns_info = resolve_attribute_namespace(attribute_rule, attr,
|
|
419
|
-
options.merge(mapper_class: mapper_class))
|
|
420
|
-
attr_name = if ns_info[:prefix]
|
|
421
|
-
"#{ns_info[:prefix]}:#{mapping_rule_name}"
|
|
422
|
-
else
|
|
423
|
-
attribute_rule.prefixed_name
|
|
424
|
-
end
|
|
425
|
-
attributes[attr_name] = value ? value.to_s : value
|
|
426
|
-
end
|
|
427
|
-
end
|
|
428
|
-
|
|
429
|
-
# Add schema_location attribute from ElementNode if present
|
|
430
|
-
# This is for the plan-based path where schema_location_attr is computed during planning
|
|
431
|
-
attributes.merge!(plan.root_node.schema_location_attr) if plan.respond_to?(:root_node) && plan.root_node&.schema_location_attr
|
|
432
|
-
|
|
433
|
-
# Determine prefix from plan
|
|
434
|
-
prefix = nil
|
|
435
|
-
option_rule = options[:rule]
|
|
436
|
-
namespace_class = if option_rule&.prefix_set? || option_rule&.namespace_set?
|
|
437
|
-
option_rule.namespace_class
|
|
438
|
-
else
|
|
439
|
-
xml_mapping.namespace_class
|
|
440
|
-
end
|
|
441
|
-
if namespace_class
|
|
442
|
-
key = namespace_class.to_key
|
|
443
|
-
ns_config = plan[:namespaces][key]
|
|
444
|
-
|
|
445
|
-
if ns_config && ns_config[:format] == :prefix
|
|
446
|
-
# Use prefix from the plan's namespace object (may be custom override)
|
|
447
|
-
prefix = ns_config[:ns_object].prefix_default
|
|
448
|
-
end
|
|
449
|
-
end
|
|
450
|
-
|
|
451
|
-
tag_name = options[:tag_name] || xml_mapping.root_element
|
|
452
|
-
return if options[:except]&.include?(tag_name)
|
|
453
|
-
|
|
454
|
-
xml.create_and_add_element(tag_name, prefix: prefix,
|
|
455
|
-
attributes: attributes.compact) do
|
|
456
|
-
# Call attribute custom methods now that element is created
|
|
457
|
-
attribute_custom_methods.each do |attribute_rule|
|
|
458
|
-
mapper_class.new.send(attribute_rule.custom_methods[:to],
|
|
459
|
-
element, xml.parent, xml)
|
|
460
|
-
end
|
|
461
|
-
|
|
462
|
-
if ordered?(element, options.merge(mapper_class: mapper_class))
|
|
463
|
-
build_ordered_element_with_plan(xml, element, plan,
|
|
464
|
-
options.merge(mapper_class: mapper_class, parent_prefix: prefix))
|
|
465
|
-
else
|
|
466
|
-
build_unordered_children_with_plan(xml, element, plan,
|
|
467
|
-
options.merge(mapper_class: mapper_class, parent_prefix: prefix))
|
|
468
|
-
end
|
|
469
|
-
end
|
|
470
|
-
end
|
|
471
|
-
|
|
472
|
-
# NOTE: build_unordered_children_with_plan and build_ordered_element_with_plan
|
|
473
|
-
# are inherited from BaseAdapter and use child_plan_for for unified plan access
|
|
474
|
-
|
|
475
|
-
def handle_nested_elements_with_plan(xml, value, rule, attribute, plan,
|
|
476
|
-
options)
|
|
477
|
-
element_options = options.merge(
|
|
478
|
-
rule: rule,
|
|
479
|
-
attribute: attribute,
|
|
480
|
-
tag_name: rule.name,
|
|
481
|
-
mapper_class: attribute.type(register), # Override with child's type
|
|
482
|
-
)
|
|
483
|
-
|
|
484
|
-
if value.is_a?(Lutaml::Model::Collection)
|
|
485
|
-
value.collection.each do |val|
|
|
486
|
-
build_element_with_plan(xml, val, plan, element_options)
|
|
487
|
-
end
|
|
488
|
-
return
|
|
489
|
-
end
|
|
490
|
-
|
|
491
|
-
case value
|
|
492
|
-
when Array
|
|
493
|
-
value.each do |val|
|
|
494
|
-
if plan
|
|
495
|
-
build_element_with_plan(xml, val, plan, element_options)
|
|
496
|
-
else
|
|
497
|
-
# Fallback for cases without plan
|
|
498
|
-
build_element(xml, val, element_options)
|
|
499
|
-
end
|
|
500
|
-
end
|
|
501
|
-
else
|
|
502
|
-
if plan
|
|
503
|
-
build_element_with_plan(xml, value, plan, element_options)
|
|
504
|
-
else
|
|
505
|
-
# Fallback for cases without plan
|
|
506
|
-
build_element(xml, value, element_options)
|
|
507
|
-
end
|
|
508
|
-
end
|
|
509
|
-
end
|
|
510
|
-
|
|
511
|
-
def add_simple_value(xml, rule, value, attribute, plan: nil,
|
|
512
|
-
mapping: nil)
|
|
513
|
-
# Apply value_map transformation BEFORE checking if should render
|
|
514
|
-
value = rule.render_value_for(value) if rule
|
|
515
|
-
|
|
516
|
-
# Handle array values by creating multiple elements
|
|
517
|
-
if value.is_a?(Array)
|
|
518
|
-
# For empty arrays, check if we should render based on render_empty option
|
|
519
|
-
if value.empty?
|
|
520
|
-
# Only create element if render_empty is set to render (not :omit)
|
|
521
|
-
if rule.render_empty?
|
|
522
|
-
# Create single empty element for the collection
|
|
523
|
-
# Determine how to render based on render_empty option
|
|
524
|
-
if rule.render_empty_as_nil?
|
|
525
|
-
# render_empty: :as_nil
|
|
526
|
-
xml.create_and_add_element(rule.name,
|
|
527
|
-
attributes: { "xsi:nil" => true },
|
|
528
|
-
prefix: nil)
|
|
529
|
-
else
|
|
530
|
-
# render_empty: :as_blank or :as_empty
|
|
531
|
-
xml.create_and_add_element(rule.name,
|
|
532
|
-
attributes: nil,
|
|
533
|
-
prefix: nil)
|
|
534
|
-
end
|
|
535
|
-
end
|
|
536
|
-
# Don't iterate over empty array
|
|
537
|
-
return
|
|
538
|
-
end
|
|
539
|
-
|
|
540
|
-
# Non-empty array: create element for each value
|
|
541
|
-
value.each do |val|
|
|
542
|
-
add_simple_value(xml, rule, val, attribute, plan: plan,
|
|
543
|
-
mapping: mapping)
|
|
544
|
-
end
|
|
545
|
-
return
|
|
546
|
-
end
|
|
547
|
-
|
|
548
|
-
# Get form_default from parent's schema (namespace class)
|
|
549
|
-
form_default = mapping&.namespace_class&.element_form_default || :qualified
|
|
550
|
-
|
|
551
|
-
# Resolve element's namespace first to know which namespace we're dealing with
|
|
552
|
-
temp_ns_info = rule.resolve_namespace(
|
|
553
|
-
attr: attribute,
|
|
554
|
-
register: register,
|
|
555
|
-
parent_ns_uri: mapping&.namespace_uri,
|
|
556
|
-
parent_ns_class: mapping&.namespace_class,
|
|
557
|
-
form_default: form_default,
|
|
558
|
-
use_prefix: false, # Temporary, just to get namespace
|
|
559
|
-
parent_prefix: nil,
|
|
560
|
-
)
|
|
561
|
-
|
|
562
|
-
element_ns_uri = temp_ns_info[:uri]
|
|
563
|
-
|
|
564
|
-
# NAMESPACE RESOLUTION: Determine if element should use prefix
|
|
565
|
-
# Cases:
|
|
566
|
-
# 1. namespace: :inherit → always use parent prefix
|
|
567
|
-
# 2. Type namespace → use Type's namespace from plan
|
|
568
|
-
# 3. Parent uses prefix format AND element has no explicit/type namespace → inherit parent
|
|
569
|
-
# 4. Element has namespace matching parent → check plan[:namespaces][ns_class]
|
|
570
|
-
# 5. Element has explicit namespace: nil → NO prefix ever
|
|
571
|
-
|
|
572
|
-
use_prefix = false
|
|
573
|
-
parent_prefix = nil
|
|
574
|
-
|
|
575
|
-
# PRIORITY: Check explicit form and prefix options FIRST
|
|
576
|
-
# These override all other considerations
|
|
577
|
-
if rule.qualified?
|
|
578
|
-
# Explicit form: :qualified - element MUST use prefix
|
|
579
|
-
use_prefix = true
|
|
580
|
-
# Find appropriate prefix for the element's namespace
|
|
581
|
-
if element_ns_uri && plan && plan[:namespaces]
|
|
582
|
-
ns_entry = plan[:namespaces].find do |_key, ns_config|
|
|
583
|
-
ns_config[:ns_object].uri == element_ns_uri
|
|
584
|
-
end
|
|
585
|
-
if ns_entry
|
|
586
|
-
_key, ns_config = ns_entry
|
|
587
|
-
parent_prefix = ns_config[:ns_object].prefix_default
|
|
588
|
-
end
|
|
589
|
-
end
|
|
590
|
-
elsif rule.unqualified?
|
|
591
|
-
# Explicit form: :unqualified - element MUST NOT use prefix
|
|
592
|
-
use_prefix = false
|
|
593
|
-
parent_prefix = nil
|
|
594
|
-
elsif rule.prefix_set?
|
|
595
|
-
# Explicit prefix option - element should use specified prefix
|
|
596
|
-
use_prefix = true
|
|
597
|
-
# If prefix is a string, use it; if true, use namespace's default prefix
|
|
598
|
-
if rule.prefix.is_a?(String)
|
|
599
|
-
parent_prefix = rule.prefix
|
|
600
|
-
elsif element_ns_uri && plan && plan[:namespaces]
|
|
601
|
-
ns_entry = plan[:namespaces].find do |_key, ns_config|
|
|
602
|
-
ns_config[:ns_object].uri == element_ns_uri
|
|
603
|
-
end
|
|
604
|
-
if ns_entry
|
|
605
|
-
_key, ns_config = ns_entry
|
|
606
|
-
parent_prefix = ns_config[:ns_object].prefix_default
|
|
607
|
-
end
|
|
608
|
-
end
|
|
609
|
-
elsif rule.namespace_param == :inherit
|
|
610
|
-
# Case 1: Explicit :inherit - always use parent format
|
|
611
|
-
use_prefix = true
|
|
612
|
-
if plan && mapping&.namespace_class
|
|
613
|
-
key = mapping.namespace_class.to_key
|
|
614
|
-
ns_config = plan[:namespaces][key]
|
|
615
|
-
if ns_config && ns_config[:format] == :prefix
|
|
616
|
-
# CRITICAL: Use the ns_object from plan (may be override with custom prefix)
|
|
617
|
-
parent_prefix = ns_config[:ns_object].prefix_default
|
|
618
|
-
end
|
|
619
|
-
end
|
|
620
|
-
elsif plan && plan[:type_namespaces] && plan[:type_namespaces][rule.to]
|
|
621
|
-
# Case 2: Type namespace - this attribute's type defines its own namespace
|
|
622
|
-
# Priority: Type namespace takes precedence over parent inheritance
|
|
623
|
-
type_ns_class = plan[:type_namespaces][rule.to]
|
|
624
|
-
key = type_ns_class.to_key
|
|
625
|
-
ns_config = plan[:namespaces][key]
|
|
626
|
-
if ns_config && ns_config[:format] == :prefix
|
|
627
|
-
use_prefix = true
|
|
628
|
-
# CRITICAL: Use ns_object from plan (may be override with custom prefix)
|
|
629
|
-
parent_prefix = ns_config[:ns_object].prefix_default
|
|
630
|
-
end
|
|
631
|
-
elsif !rule.namespace_set? && !element_ns_uri && mapping&.namespace_class && plan
|
|
632
|
-
# Case 3: NEW - Format Matching Rule
|
|
633
|
-
# When parent uses prefix format AND element has no explicit namespace AND no type namespace,
|
|
634
|
-
# element inherits parent's namespace and prefix for consistent formatting.
|
|
635
|
-
# This handles the test case where children should match parent's serialization format.
|
|
636
|
-
# IMPORTANT: Only applies when element_form_default is :qualified
|
|
637
|
-
key = mapping.namespace_class.to_key
|
|
638
|
-
ns_config = plan[:namespaces][key]
|
|
639
|
-
if ns_config && ns_config[:format] == :prefix && form_default == :qualified
|
|
640
|
-
# Parent is using prefix format AND schema requires qualified elements
|
|
641
|
-
use_prefix = true
|
|
642
|
-
parent_prefix = ns_config[:ns_object].prefix_default
|
|
643
|
-
# Override element_ns_uri to parent's URI for proper resolution
|
|
644
|
-
element_ns_uri = mapping.namespace_uri
|
|
645
|
-
end
|
|
646
|
-
elsif element_ns_uri
|
|
647
|
-
# Case 4: Element has explicit namespace - check if it's in prefix mode
|
|
648
|
-
# Need to find the namespace class by URI to look up config
|
|
649
|
-
if plan && plan[:namespaces]
|
|
650
|
-
# Find namespace entry that matches this URI
|
|
651
|
-
ns_entry = plan[:namespaces].find do |_key, ns_config|
|
|
652
|
-
ns_config[:ns_object].uri == element_ns_uri
|
|
653
|
-
end
|
|
654
|
-
if ns_entry
|
|
655
|
-
_key, ns_config = ns_entry
|
|
656
|
-
use_prefix = ns_config[:format] == :prefix
|
|
657
|
-
parent_prefix = ns_config[:ns_object].prefix_default if use_prefix
|
|
658
|
-
end
|
|
659
|
-
end
|
|
660
|
-
elsif !rule.namespace_set? && element_ns_uri && element_ns_uri == mapping&.namespace_uri
|
|
661
|
-
# Case 5: Element has SAME namespace as parent (not nil, not unqualified)
|
|
662
|
-
# Element has a resolved namespace that matches parent -> inherit parent format
|
|
663
|
-
# Truly unqualified elements (element_ns_uri.nil?) do NOT inherit
|
|
664
|
-
if plan && mapping&.namespace_class
|
|
665
|
-
key = mapping.namespace_class.to_key
|
|
666
|
-
ns_config = plan[:namespaces][key]
|
|
667
|
-
if ns_config && ns_config[:format] == :prefix
|
|
668
|
-
use_prefix = true
|
|
669
|
-
# CRITICAL: Use the ns_object from plan (may be override with custom prefix)
|
|
670
|
-
parent_prefix = ns_config[:ns_object].prefix_default
|
|
671
|
-
end
|
|
672
|
-
end
|
|
673
|
-
end
|
|
674
|
-
# Case 6: explicit namespace: nil is handled by namespace_set? && namespace_param == nil
|
|
675
|
-
# Case 7: truly unqualified (element_ns_uri.nil?) falls through with use_prefix = false
|
|
676
|
-
|
|
677
|
-
# Now resolve with correct use_prefix
|
|
678
|
-
ns_info = rule.resolve_namespace(
|
|
679
|
-
attr: attribute,
|
|
680
|
-
register: register,
|
|
681
|
-
parent_ns_uri: mapping&.namespace_uri,
|
|
682
|
-
parent_ns_class: mapping&.namespace_class,
|
|
683
|
-
form_default: form_default,
|
|
684
|
-
use_prefix: use_prefix,
|
|
685
|
-
parent_prefix: parent_prefix,
|
|
686
|
-
)
|
|
687
|
-
|
|
688
|
-
# Use resolved namespace directly, BUT handle special cases:
|
|
689
|
-
# 1. namespace: :inherit → ALWAYS use parent prefix (resolved has parent URI)
|
|
690
|
-
# 2. Truly unqualified elements (element_ns_uri==nil) → NO prefix unless :inherit
|
|
691
|
-
resolved_prefix = if rule.namespace_param == :inherit || (use_prefix && parent_prefix)
|
|
692
|
-
parent_prefix
|
|
693
|
-
else
|
|
694
|
-
ns_info[:prefix]
|
|
695
|
-
end
|
|
696
|
-
|
|
697
|
-
# Prepare attributes (no xmlns declaration - handled by DeclarationPlanner)
|
|
698
|
-
attributes = {}
|
|
699
|
-
|
|
700
|
-
# Check if this namespace needs local declaration (out of scope)
|
|
701
|
-
if resolved_prefix && plan && plan[:namespaces]
|
|
702
|
-
# Find the namespace config for this prefix/URI
|
|
703
|
-
ns_entry = plan[:namespaces].find do |_key, ns_config|
|
|
704
|
-
ns_config[:ns_object].prefix_default == resolved_prefix ||
|
|
705
|
-
(ns_info[:uri] && ns_config[:ns_object].uri == ns_info[:uri])
|
|
706
|
-
end
|
|
707
|
-
|
|
708
|
-
if ns_entry
|
|
709
|
-
_key, ns_config = ns_entry
|
|
710
|
-
# If namespace is marked for local declaration, add xmlns attribute
|
|
711
|
-
if ns_config[:declared_at] == :local_on_use
|
|
712
|
-
xmlns_attr = "xmlns:#{resolved_prefix}"
|
|
713
|
-
attributes[xmlns_attr] = ns_config[:ns_object].uri
|
|
714
|
-
end
|
|
715
|
-
end
|
|
716
|
-
end
|
|
717
|
-
|
|
718
|
-
if value.nil?
|
|
719
|
-
# Check render_nil option to determine how to render nil value
|
|
720
|
-
if rule.render_nil_as_blank? || rule.render_nil_as_empty?
|
|
721
|
-
# render_nil: :as_blank or :as_empty - create blank element without xsi:nil
|
|
722
|
-
xml.create_and_add_element(rule.name,
|
|
723
|
-
attributes: attributes,
|
|
724
|
-
prefix: resolved_prefix)
|
|
725
|
-
else
|
|
726
|
-
# render_nil: :as_nil or default - create element with xsi:nil="true"
|
|
727
|
-
xml.create_and_add_element(rule.name,
|
|
728
|
-
attributes: attributes.merge({ "xsi:nil" => true }),
|
|
729
|
-
prefix: resolved_prefix)
|
|
730
|
-
end
|
|
731
|
-
elsif ::Lutaml::Model::Utils.uninitialized?(value)
|
|
732
|
-
# Handle uninitialized values - don't try to serialize them as text
|
|
733
|
-
# This should not normally happen as render? should filter these out
|
|
734
|
-
# But if render_omitted is set, we might reach here
|
|
735
|
-
nil
|
|
736
|
-
elsif ::Lutaml::Model::Utils.empty?(value)
|
|
737
|
-
xml.create_and_add_element(rule.name,
|
|
738
|
-
attributes: attributes,
|
|
739
|
-
prefix: resolved_prefix)
|
|
740
|
-
elsif rule.raw_mapping?
|
|
741
|
-
xml.add_xml_fragment(xml, value)
|
|
742
|
-
elsif value.is_a?(::Hash) && attribute&.type(register) == Lutaml::Model::Type::Hash
|
|
743
|
-
# Check if value is Hash type that needs wrapper - do this BEFORE any wrapping/serialization
|
|
744
|
-
# Value is already transformed by ExportTransformer before reaching here
|
|
745
|
-
xml.create_and_add_element(rule.name,
|
|
746
|
-
attributes: attributes,
|
|
747
|
-
prefix: resolved_prefix) do
|
|
748
|
-
value.each do |key, val|
|
|
749
|
-
xml.create_and_add_element(key.to_s) do
|
|
750
|
-
xml.add_text(xml, val.to_s)
|
|
751
|
-
end
|
|
752
|
-
end
|
|
753
|
-
end
|
|
754
|
-
else
|
|
755
|
-
xml.create_and_add_element(rule.name,
|
|
756
|
-
attributes: attributes,
|
|
757
|
-
prefix: resolved_prefix) do
|
|
758
|
-
add_value(xml, value, attribute, cdata: rule.cdata)
|
|
759
|
-
end
|
|
760
|
-
end
|
|
761
|
-
end
|
|
762
|
-
|
|
763
|
-
# Build XML from XmlElement structure
|
|
764
|
-
#
|
|
765
|
-
# @param xml [Builder] the XML builder
|
|
766
|
-
|
|
767
|
-
private
|
|
768
|
-
|
|
769
|
-
def determine_encoding(options)
|
|
770
|
-
options[:encoding] ||
|
|
771
|
-
options[:parse_encoding] ||
|
|
772
|
-
@encoding ||
|
|
773
|
-
"UTF-8"
|
|
774
|
-
end
|
|
775
|
-
|
|
776
|
-
def build_ordered_element(builder, element, options = {})
|
|
777
|
-
mapper_class = determine_mapper_class(element, options)
|
|
778
|
-
xml_mapping = mapper_class.mappings_for(:xml)
|
|
779
|
-
return builder unless xml_mapping
|
|
780
|
-
|
|
781
|
-
attributes = build_attributes(element, xml_mapping, options).compact
|
|
782
|
-
prefix = determine_namespace_prefix(options, xml_mapping)
|
|
783
|
-
tag_name = options[:tag_name] || xml_mapping.root_element
|
|
784
|
-
|
|
785
|
-
builder.create_and_add_element(tag_name, prefix: prefix,
|
|
786
|
-
attributes: attributes) do |el|
|
|
787
|
-
process_element_order(el, element, xml_mapping, mapper_class,
|
|
788
|
-
options)
|
|
789
|
-
end
|
|
790
|
-
end
|
|
791
|
-
|
|
792
|
-
def process_element_order(builder, element, xml_mapping, mapper_class,
|
|
793
|
-
options)
|
|
794
|
-
index_hash = {}
|
|
795
|
-
content = []
|
|
796
|
-
|
|
797
|
-
element.element_order.each do |object|
|
|
798
|
-
process_ordered_object(builder, element, object, xml_mapping, mapper_class,
|
|
799
|
-
index_hash, content, options)
|
|
800
|
-
end
|
|
801
|
-
|
|
802
|
-
builder.text(content.join)
|
|
803
|
-
end
|
|
804
|
-
|
|
805
|
-
def process_ordered_object(builder, element, object, xml_mapping, mapper_class,
|
|
806
|
-
index_hash, content, options)
|
|
807
|
-
curr_index = increment_object_index(index_hash, object)
|
|
808
|
-
element_rule = xml_mapping.find_by_name(object.name,
|
|
809
|
-
type: object.type,
|
|
810
|
-
node_type: object.node_type)
|
|
811
|
-
|
|
812
|
-
return if skip_element_rule?(element_rule, options)
|
|
813
|
-
|
|
814
|
-
attribute_def = attribute_definition_for(element, element_rule,
|
|
815
|
-
mapper_class: mapper_class)
|
|
816
|
-
value = attribute_value_for(element, element_rule)
|
|
817
|
-
|
|
818
|
-
return if skip_cdata_text?(element_rule, xml_mapping, object)
|
|
819
|
-
|
|
820
|
-
handle_ordered_element_content(builder, element, element_rule, xml_mapping,
|
|
821
|
-
attribute_def, value, curr_index, content, options, mapper_class)
|
|
822
|
-
end
|
|
823
|
-
|
|
824
|
-
def increment_object_index(index_hash, object)
|
|
825
|
-
object_key = "#{object.name}-#{object.type}"
|
|
826
|
-
index_hash[object_key] ||= -1
|
|
827
|
-
index_hash[object_key] += 1
|
|
828
|
-
end
|
|
829
|
-
|
|
830
|
-
def skip_element_rule?(element_rule, options)
|
|
831
|
-
element_rule.nil? || options[:except]&.include?(element_rule.to)
|
|
832
|
-
end
|
|
833
|
-
|
|
834
|
-
def skip_cdata_text?(element_rule, xml_mapping, object)
|
|
835
|
-
element_rule == xml_mapping.content_mapping && element_rule.cdata && object.text?
|
|
836
|
-
end
|
|
837
|
-
|
|
838
|
-
def handle_ordered_element_content(builder, element, element_rule, xml_mapping,
|
|
839
|
-
attribute_def, value, curr_index, content, options, mapper_class)
|
|
840
|
-
if element_rule == xml_mapping.content_mapping
|
|
841
|
-
handle_ordered_content_text(builder, element, element_rule,
|
|
842
|
-
xml_mapping, curr_index, content)
|
|
843
|
-
elsif !value.nil? || element_rule.render_nil?
|
|
844
|
-
add_ordered_element_value(builder, element, attribute_def, value, curr_index,
|
|
845
|
-
element_rule, options, mapper_class)
|
|
846
|
-
end
|
|
847
|
-
end
|
|
848
|
-
|
|
849
|
-
def handle_ordered_content_text(builder, element, element_rule,
|
|
850
|
-
xml_mapping, curr_index, content)
|
|
851
|
-
text = xml_mapping.content_mapping.serialize(element)
|
|
852
|
-
text = text[curr_index] if text.is_a?(Array)
|
|
853
|
-
|
|
854
|
-
if element.mixed?
|
|
855
|
-
if element_rule.cdata
|
|
856
|
-
return builder.cdata(text)
|
|
857
|
-
else
|
|
858
|
-
return builder.text(text)
|
|
859
|
-
end
|
|
860
|
-
end
|
|
861
|
-
|
|
862
|
-
content << text
|
|
863
|
-
end
|
|
864
|
-
|
|
865
|
-
def add_ordered_element_value(builder, element, attribute_def, value, curr_index,
|
|
866
|
-
element_rule, options, mapper_class)
|
|
867
|
-
value = value[curr_index] if attribute_def.collection?
|
|
868
|
-
|
|
869
|
-
add_to_xml(builder, element, nil, value,
|
|
870
|
-
options.merge(attribute: attribute_def, rule: element_rule,
|
|
871
|
-
mapper_class: mapper_class))
|
|
872
|
-
end
|
|
12
|
+
MOXML_ADAPTER = Moxml::Adapter::Rexml
|
|
13
|
+
BUILDER_CLASS = Builder::Rexml
|
|
14
|
+
PARSED_ELEMENT_CLASS = Rexml::Element
|
|
15
|
+
EMPTY_DOCUMENT_ERROR_MESSAGE = "Malformed XML: Unable to parse " \
|
|
16
|
+
"the provided XML document. The document structure is invalid " \
|
|
17
|
+
"or incomplete."
|
|
18
|
+
EMPTY_DOCUMENT_ERROR_TYPE = :parse_exception
|
|
873
19
|
end
|
|
874
20
|
end
|
|
875
21
|
end
|