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,856 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require "oga"
|
|
2
4
|
require "moxml/adapter/oga"
|
|
5
|
+
require_relative "base_adapter"
|
|
3
6
|
|
|
4
7
|
module Lutaml
|
|
5
8
|
module Xml
|
|
6
9
|
module Adapter
|
|
7
10
|
class OgaAdapter < BaseAdapter
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
TEXT_CLASSES = [Moxml::Text, Moxml::Cdata].freeze
|
|
12
|
-
|
|
13
|
-
def self.parse(xml, options = {})
|
|
14
|
-
enc = encoding(xml, options)
|
|
15
|
-
# Oga requires UTF-8 encoded input; convert from other encodings
|
|
16
|
-
xml = xml.encode("UTF-8") unless xml.encoding == Encoding::UTF_8
|
|
17
|
-
parsed = Moxml::Adapter::Oga.parse(xml, encoding: enc)
|
|
18
|
-
root_element = parsed.root
|
|
19
|
-
|
|
20
|
-
# Validate that we have a root element
|
|
21
|
-
if root_element.nil?
|
|
22
|
-
raise Lutaml::Model::InvalidFormatError.new(
|
|
23
|
-
:xml,
|
|
24
|
-
"Document has no root element. " \
|
|
25
|
-
"The XML may be empty, contain only whitespace, " \
|
|
26
|
-
"or consist only of an XML declaration.",
|
|
27
|
-
)
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
# Extract DOCTYPE information
|
|
31
|
-
# Moxml/Oga doesn't directly expose DOCTYPE, extract from raw XML
|
|
32
|
-
doctype_info = extract_doctype_from_xml(xml)
|
|
33
|
-
|
|
34
|
-
@root = Oga::Element.new(root_element)
|
|
35
|
-
@root.processing_instructions = extract_document_processing_instructions(parsed)
|
|
36
|
-
new(@root, enc, doctype: doctype_info)
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def to_xml(options = {})
|
|
40
|
-
builder_options = {}
|
|
41
|
-
encoding = determine_encoding(options)
|
|
42
|
-
builder_options[:encoding] = encoding if encoding
|
|
43
|
-
|
|
44
|
-
builder = Builder::Oga.build(builder_options) do |xml|
|
|
45
|
-
if @root.is_a?(Oga::Element)
|
|
46
|
-
@root.build_xml(xml)
|
|
47
|
-
elsif @root.is_a?(Lutaml::Xml::DataModel::XmlElement)
|
|
48
|
-
# XmlDataModel MUST go through Three-Phase Architecture
|
|
49
|
-
mapper_class = options[:mapper_class] || @root.class
|
|
50
|
-
xml_mapping = mapper_class.mappings_for(:xml)
|
|
51
|
-
|
|
52
|
-
# Phase 1: Collect namespace needs from XmlElement tree
|
|
53
|
-
collector = NamespaceCollector.new(register)
|
|
54
|
-
needs = collector.collect(@root, xml_mapping,
|
|
55
|
-
mapper_class: mapper_class)
|
|
56
|
-
|
|
57
|
-
# Phase 2: Plan namespace declarations with hoisting
|
|
58
|
-
planner = DeclarationPlanner.new(register)
|
|
59
|
-
plan = planner.plan(@root, xml_mapping, needs,
|
|
60
|
-
options: options)
|
|
61
|
-
|
|
62
|
-
# Phase 3: Build with plan (TREE-BASED for XmlElement)
|
|
63
|
-
build_xml_element_with_plan(xml, @root, plan, options)
|
|
64
|
-
else
|
|
65
|
-
# THREE-PHASE ARCHITECTURE
|
|
66
|
-
mapper_class = options[:mapper_class] || @root.class
|
|
67
|
-
xml_mapping = mapper_class.mappings_for(:xml)
|
|
68
|
-
|
|
69
|
-
# Check if model has map_all with custom methods
|
|
70
|
-
# Custom methods work with model instances, not XmlElement trees
|
|
71
|
-
has_custom_map_all = xml_mapping.raw_mapping&.custom_methods &&
|
|
72
|
-
xml_mapping.raw_mapping.custom_methods[:to]
|
|
73
|
-
|
|
74
|
-
if has_custom_map_all
|
|
75
|
-
# Use legacy path for custom methods
|
|
76
|
-
collector = NamespaceCollector.new(register)
|
|
77
|
-
needs = collector.collect(@root, xml_mapping,
|
|
78
|
-
mapper_class: mapper_class)
|
|
79
|
-
|
|
80
|
-
planner = DeclarationPlanner.new(register)
|
|
81
|
-
plan = planner.plan(@root, xml_mapping, needs, options: options)
|
|
82
|
-
|
|
83
|
-
build_element_with_plan(xml, @root, plan, options)
|
|
84
|
-
else
|
|
85
|
-
# Step 1: Transform model to XmlElement tree
|
|
86
|
-
transformation = mapper_class.transformation_for(:xml, register)
|
|
87
|
-
xml_element = transformation.transform(@root, options)
|
|
88
|
-
|
|
89
|
-
# Step 2: Collect namespace needs from XmlElement tree
|
|
90
|
-
collector = NamespaceCollector.new(register)
|
|
91
|
-
needs = collector.collect(xml_element, xml_mapping,
|
|
92
|
-
mapper_class: mapper_class)
|
|
93
|
-
|
|
94
|
-
# Step 3: Plan declarations (builds ElementNode tree)
|
|
95
|
-
planner = DeclarationPlanner.new(register)
|
|
96
|
-
plan = planner.plan(xml_element, xml_mapping, needs,
|
|
97
|
-
options: options)
|
|
98
|
-
|
|
99
|
-
# Step 4: Render using tree (NEW - parallel traversal)
|
|
100
|
-
build_xml_element_with_plan(xml, xml_element, plan, options)
|
|
101
|
-
end
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
xml_data = builder.to_xml
|
|
105
|
-
|
|
106
|
-
result = ""
|
|
107
|
-
# Use DeclarationHandler methods instead of Document#declaration
|
|
108
|
-
# Include declaration when encoding is specified OR when declaration is requested
|
|
109
|
-
if (options[:encoding] && !options[:encoding].nil?) || options[:declaration]
|
|
110
|
-
result += generate_declaration(options)
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
# Add DOCTYPE if present - use DeclarationHandler method
|
|
114
|
-
doctype_to_use = options[:doctype] || @doctype
|
|
115
|
-
if doctype_to_use && !options[:omit_doctype]
|
|
116
|
-
result += generate_doctype_declaration(doctype_to_use)
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
result += xml_data
|
|
120
|
-
|
|
121
|
-
# Encode result to desired encoding (Oga outputs UTF-8 by default)
|
|
122
|
-
if encoding && encoding != Encoding::UTF_8
|
|
123
|
-
result = result.encode(encoding)
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
result
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
# Build element using prepared namespace declaration plan
|
|
130
|
-
#
|
|
131
|
-
# @param xml [Builder] the XML builder
|
|
132
|
-
# @param element [Object] the model instance
|
|
133
|
-
# @param plan [Hash] the declaration plan from DeclarationPlanner
|
|
134
|
-
# @param options [Hash] serialization options
|
|
135
|
-
def build_element_with_plan(xml, element, plan, options = {})
|
|
136
|
-
mapper_class = options[:mapper_class] || element.class
|
|
137
|
-
xml_mapping = mapper_class.mappings_for(:xml)
|
|
138
|
-
return xml unless xml_mapping
|
|
139
|
-
|
|
140
|
-
# TYPE-ONLY MODELS: No element wrapper, serialize children directly
|
|
141
|
-
# BUT if we have a tag_name in options, that means parent wants a wrapper
|
|
142
|
-
plan ||= {
|
|
143
|
-
namespaces: {},
|
|
144
|
-
children_plans: {},
|
|
145
|
-
type_namespaces: {},
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
if xml_mapping.no_element?
|
|
149
|
-
# If parent provided a tag_name, create that wrapper first
|
|
150
|
-
if options[:tag_name]
|
|
151
|
-
xml.create_and_add_element(options[:tag_name]) do |inner_xml|
|
|
152
|
-
# Serialize type-only model's children inside parent's wrapper
|
|
153
|
-
xml_mapping.elements.each do |element_rule|
|
|
154
|
-
next if options[:except]&.include?(element_rule.to)
|
|
155
|
-
|
|
156
|
-
attribute_def = mapper_class.attributes[element_rule.to]
|
|
157
|
-
next unless attribute_def
|
|
158
|
-
|
|
159
|
-
value = element.send(element_rule.to)
|
|
160
|
-
next unless element_rule.render?(value, element)
|
|
161
|
-
|
|
162
|
-
# For type-only models, children plans may not be available
|
|
163
|
-
# Serialize children directly
|
|
164
|
-
if value && attribute_def.type(register)&.<=(Lutaml::Model::Serialize)
|
|
165
|
-
# Nested model - recursively build it
|
|
166
|
-
child_plan = plan.child_plan(element_rule.to) || DeclarationPlan.empty
|
|
167
|
-
build_element_with_plan(
|
|
168
|
-
inner_xml,
|
|
169
|
-
value,
|
|
170
|
-
child_plan,
|
|
171
|
-
{ mapper_class: attribute_def.type(register),
|
|
172
|
-
tag_name: element_rule.name },
|
|
173
|
-
)
|
|
174
|
-
else
|
|
175
|
-
# Simple value - create element directly
|
|
176
|
-
inner_xml.create_and_add_element(element_rule.name) do
|
|
177
|
-
add_value(inner_xml, value, attribute_def,
|
|
178
|
-
cdata: element_rule.cdata)
|
|
179
|
-
end
|
|
180
|
-
end
|
|
181
|
-
end
|
|
182
|
-
end
|
|
183
|
-
else
|
|
184
|
-
# No wrapper at all - serialize children directly (for root-level type-only)
|
|
185
|
-
xml_mapping.elements.each do |element_rule|
|
|
186
|
-
next if options[:except]&.include?(element_rule.to)
|
|
187
|
-
|
|
188
|
-
attribute_def = mapper_class.attributes[element_rule.to]
|
|
189
|
-
next unless attribute_def
|
|
190
|
-
|
|
191
|
-
value = element.send(element_rule.to)
|
|
192
|
-
next unless element_rule.render?(value, element)
|
|
193
|
-
|
|
194
|
-
child_plan = plan.child_plan(element_rule.to)
|
|
195
|
-
|
|
196
|
-
if value && attribute_def.type(register)&.<=(Lutaml::Model::Serialize)
|
|
197
|
-
handle_nested_elements_with_plan(
|
|
198
|
-
xml,
|
|
199
|
-
value,
|
|
200
|
-
element_rule,
|
|
201
|
-
attribute_def,
|
|
202
|
-
child_plan,
|
|
203
|
-
options,
|
|
204
|
-
)
|
|
205
|
-
else
|
|
206
|
-
add_simple_value(xml, element_rule, value, attribute_def,
|
|
207
|
-
plan: plan, mapping: xml_mapping)
|
|
208
|
-
end
|
|
209
|
-
end
|
|
210
|
-
end
|
|
211
|
-
return xml
|
|
212
|
-
end
|
|
213
|
-
|
|
214
|
-
# Use xmlns declarations from plan
|
|
215
|
-
attributes = {}
|
|
216
|
-
|
|
217
|
-
# Apply namespace declarations from plan using extracted module
|
|
218
|
-
attributes.merge!(NamespaceDeclarationBuilder.build_xmlns_attributes(plan))
|
|
219
|
-
|
|
220
|
-
# Add regular attributes (non-xmlns)
|
|
221
|
-
|
|
222
|
-
xml_mapping.attributes.each do |attribute_rule|
|
|
223
|
-
next if attribute_rule.custom_methods[:to] ||
|
|
224
|
-
options[:except]&.include?(attribute_rule.to)
|
|
225
|
-
|
|
226
|
-
mapping_rule_name = if attribute_rule.multiple_mappings?
|
|
227
|
-
attribute_rule.name.first
|
|
228
|
-
else
|
|
229
|
-
attribute_rule.name
|
|
230
|
-
end
|
|
231
|
-
|
|
232
|
-
attr = attribute_definition_for(element, attribute_rule,
|
|
233
|
-
mapper_class: mapper_class)
|
|
234
|
-
value = attribute_rule.to_value_for(element)
|
|
235
|
-
|
|
236
|
-
# Handle as_list and delimiter BEFORE serialization for array values
|
|
237
|
-
# These features convert arrays to delimited strings before serialization
|
|
238
|
-
if value.is_a?(Array)
|
|
239
|
-
if attribute_rule.as_list && attribute_rule.as_list[:export]
|
|
240
|
-
value = attribute_rule.as_list[:export].call(value)
|
|
241
|
-
elsif attribute_rule.delimiter
|
|
242
|
-
value = value.join(attribute_rule.delimiter)
|
|
243
|
-
end
|
|
244
|
-
end
|
|
245
|
-
|
|
246
|
-
value = attr.serialize(value, :xml, register) if attr
|
|
247
|
-
value = ExportTransformer.call(value, attribute_rule, attr,
|
|
248
|
-
format: :xml)
|
|
249
|
-
|
|
250
|
-
if render_element?(attribute_rule, element, value)
|
|
251
|
-
# Resolve attribute namespace using extracted module
|
|
252
|
-
ns_info = AttributeNamespaceResolver.resolve(
|
|
253
|
-
rule: attribute_rule,
|
|
254
|
-
attribute: attr,
|
|
255
|
-
plan: plan,
|
|
256
|
-
mapper_class: mapper_class,
|
|
257
|
-
register: register,
|
|
258
|
-
)
|
|
259
|
-
|
|
260
|
-
# Build qualified attribute name based on W3C semantics
|
|
261
|
-
attr_name = AttributeNamespaceResolver.build_qualified_name(
|
|
262
|
-
ns_info,
|
|
263
|
-
mapping_rule_name,
|
|
264
|
-
attribute_rule,
|
|
265
|
-
)
|
|
266
|
-
attributes[attr_name] = value ? value.to_s : value
|
|
267
|
-
|
|
268
|
-
# Add local xmlns declaration if needed
|
|
269
|
-
if ns_info[:needs_local_declaration]
|
|
270
|
-
attributes[ns_info[:local_xmlns_attr]] =
|
|
271
|
-
ns_info[:local_xmlns_uri]
|
|
272
|
-
end
|
|
273
|
-
end
|
|
274
|
-
end
|
|
275
|
-
|
|
276
|
-
# Add schema_location attribute from ElementNode if present
|
|
277
|
-
# This is for the plan-based path where schema_location_attr is computed during planning
|
|
278
|
-
attributes.merge!(plan.root_node.schema_location_attr) if plan&.root_node&.schema_location_attr
|
|
279
|
-
|
|
280
|
-
# Determine prefix from plan using extracted module
|
|
281
|
-
prefix_info = ElementPrefixResolver.resolve(mapping: xml_mapping,
|
|
282
|
-
plan: plan)
|
|
283
|
-
prefix = prefix_info[:prefix]
|
|
284
|
-
|
|
285
|
-
tag_name = options[:tag_name] || xml_mapping.root_element
|
|
286
|
-
return if options[:except]&.include?(tag_name)
|
|
287
|
-
|
|
288
|
-
xml.create_and_add_element(tag_name, prefix: prefix,
|
|
289
|
-
attributes: attributes.compact) do
|
|
290
|
-
if ordered?(element, options.merge(mapper_class: mapper_class))
|
|
291
|
-
build_ordered_element_with_plan(xml, element, plan,
|
|
292
|
-
options.merge(
|
|
293
|
-
mapper_class: mapper_class,
|
|
294
|
-
parent_ns_decl: prefix_info[:ns_decl],
|
|
295
|
-
))
|
|
296
|
-
else
|
|
297
|
-
build_unordered_children_with_plan(xml, element, plan,
|
|
298
|
-
options.merge(
|
|
299
|
-
mapper_class: mapper_class,
|
|
300
|
-
parent_ns_decl: prefix_info[:ns_decl],
|
|
301
|
-
))
|
|
302
|
-
end
|
|
303
|
-
end
|
|
304
|
-
end
|
|
305
|
-
|
|
306
|
-
# Build XML from XmlDataModel::XmlElement structure
|
|
307
|
-
#
|
|
308
|
-
# @param xml [Builder] XML builder
|
|
309
|
-
# @param element [XmlDataModel::XmlElement] element to build
|
|
310
|
-
# @param parent_uses_default_ns [Boolean] parent uses default namespace format
|
|
311
|
-
# @param parent_element_form_default [Symbol] parent's element_form_default
|
|
312
|
-
# @param parent_namespace_class [Class] parent's namespace class
|
|
313
|
-
def build_xml_element(xml, element, parent_uses_default_ns: false,
|
|
314
|
-
parent_element_form_default: nil, parent_namespace_class: nil)
|
|
315
|
-
# Prepare attributes hash
|
|
316
|
-
attributes = {}
|
|
317
|
-
|
|
318
|
-
# Determine if attributes should be qualified based on element's namespace
|
|
319
|
-
element_ns_class = element.namespace_class
|
|
320
|
-
attribute_form_default = element_ns_class&.attribute_form_default || :unqualified
|
|
321
|
-
element_prefix = element_ns_class&.prefix_default
|
|
322
|
-
|
|
323
|
-
# Get element_form_default for children
|
|
324
|
-
this_element_form_default = element_ns_class&.element_form_default || :unqualified
|
|
325
|
-
|
|
326
|
-
# Add regular attributes
|
|
327
|
-
element.attributes.each do |attr|
|
|
328
|
-
# Determine attribute name with namespace consideration
|
|
329
|
-
attr_name = if attr.namespace_class
|
|
330
|
-
# Check if attribute is in SAME namespace as element
|
|
331
|
-
if attr.namespace_class == element_ns_class && attribute_form_default == :unqualified
|
|
332
|
-
# Same namespace + unqualified → NO prefix (W3C rule)
|
|
333
|
-
attr.name
|
|
334
|
-
else
|
|
335
|
-
# Different namespace OR qualified → use prefix
|
|
336
|
-
attr_prefix = attr.namespace_class.prefix_default
|
|
337
|
-
attr_prefix ? "#{attr_prefix}:#{attr.name}" : attr.name
|
|
338
|
-
end
|
|
339
|
-
elsif attribute_form_default == :qualified && element_prefix
|
|
340
|
-
# Attribute inherits element's namespace when qualified
|
|
341
|
-
"#{element_prefix}:#{attr.name}"
|
|
342
|
-
else
|
|
343
|
-
# Unqualified attribute
|
|
344
|
-
attr.name
|
|
345
|
-
end
|
|
346
|
-
attributes[attr_name] = attr.value
|
|
347
|
-
end
|
|
348
|
-
|
|
349
|
-
# Determine element name with namespace prefix
|
|
350
|
-
tag_name = element.name
|
|
351
|
-
|
|
352
|
-
# Priority 2.5: Child namespace different from parent's default namespace
|
|
353
|
-
# MUST use prefix format to distinguish from parent
|
|
354
|
-
child_needs_prefix = if element_ns_class && parent_namespace_class &&
|
|
355
|
-
element_ns_class != parent_namespace_class && parent_uses_default_ns
|
|
356
|
-
element_prefix # Use child's prefix
|
|
357
|
-
end
|
|
358
|
-
|
|
359
|
-
# CRITICAL FIX: element_form_default: :qualified means child elements inherit parent's namespace PREFIX
|
|
360
|
-
# even when child has NO explicit namespace_class
|
|
361
|
-
prefix = if child_needs_prefix
|
|
362
|
-
# Priority 2.5 takes precedence
|
|
363
|
-
child_needs_prefix
|
|
364
|
-
elsif element_ns_class && element_prefix
|
|
365
|
-
# Element has explicit prefix_default - use prefix format
|
|
366
|
-
element_prefix
|
|
367
|
-
elsif !element_ns_class && parent_element_form_default == :qualified && parent_namespace_class&.prefix_default
|
|
368
|
-
# Child has NO namespace, but parent has :qualified form_default
|
|
369
|
-
# Child should INHERIT parent's namespace PREFIX
|
|
370
|
-
parent_namespace_class.prefix_default
|
|
371
|
-
else
|
|
372
|
-
# No prefix (default format or no parent namespace)
|
|
373
|
-
nil
|
|
374
|
-
end
|
|
375
|
-
|
|
376
|
-
# Track if THIS element uses default namespace format for children
|
|
377
|
-
this_element_uses_default_ns = false
|
|
378
|
-
|
|
379
|
-
# Add namespace declaration if element has namespace
|
|
380
|
-
if element.namespace_class
|
|
381
|
-
ns_uri = element.namespace_class.uri
|
|
382
|
-
|
|
383
|
-
if prefix
|
|
384
|
-
attributes["xmlns:#{prefix}"] = ns_uri
|
|
385
|
-
# W3C Compliance: xmlns="" only needed for blank namespace children
|
|
386
|
-
# Prefixed children are already in different namespace from parent's default
|
|
387
|
-
else
|
|
388
|
-
attributes["xmlns"] = ns_uri
|
|
389
|
-
this_element_uses_default_ns = true
|
|
390
|
-
end
|
|
391
|
-
elsif parent_uses_default_ns
|
|
392
|
-
# W3C Compliance: Element has no namespace (blank namespace)
|
|
393
|
-
# Check if should inherit parent's namespace based on element_form_default
|
|
394
|
-
# Parent uses default namespace format
|
|
395
|
-
if parent_element_form_default == :qualified
|
|
396
|
-
# Child should INHERIT parent's namespace - no xmlns="" needed
|
|
397
|
-
# The child is in parent namespace (qualified)
|
|
398
|
-
else
|
|
399
|
-
# Parent's element_form_default is :unqualified - child in blank namespace
|
|
400
|
-
# Add xmlns="" to explicitly opt out of parent's default namespace
|
|
401
|
-
attributes["xmlns"] = ""
|
|
402
|
-
end
|
|
403
|
-
end
|
|
404
|
-
|
|
405
|
-
# Check if element was created from nil value with render_nil option
|
|
406
|
-
# Add xsi:nil="true" attribute for W3C compliance
|
|
407
|
-
if element.respond_to?(:xsi_nil) && element.xsi_nil
|
|
408
|
-
attributes["xsi:nil"] = true
|
|
409
|
-
end
|
|
410
|
-
|
|
411
|
-
# Create element
|
|
412
|
-
xml.create_and_add_element(tag_name, attributes: attributes,
|
|
413
|
-
prefix: prefix) do |inner_xml|
|
|
414
|
-
# Handle raw content (map_all directive)
|
|
415
|
-
# If @raw_content exists, add as raw XML
|
|
416
|
-
has_raw_content = false
|
|
417
|
-
if element.respond_to?(:raw_content)
|
|
418
|
-
raw_content = element.raw_content
|
|
419
|
-
if raw_content && !raw_content.to_s.empty?
|
|
420
|
-
inner_xml.add_xml_fragment(inner_xml, raw_content.to_s)
|
|
421
|
-
has_raw_content = true
|
|
422
|
-
end
|
|
423
|
-
end
|
|
424
|
-
|
|
425
|
-
# Skip text content and children if we have raw content
|
|
426
|
-
unless has_raw_content
|
|
427
|
-
# Add text content if present
|
|
428
|
-
if element.text_content
|
|
429
|
-
if element.cdata
|
|
430
|
-
inner_xml.cdata(element.text_content.to_s)
|
|
431
|
-
else
|
|
432
|
-
inner_xml.text(element.text_content.to_s)
|
|
433
|
-
end
|
|
434
|
-
end
|
|
435
|
-
|
|
436
|
-
# Recursively build child elements, passing namespace context
|
|
437
|
-
element.children.each do |child|
|
|
438
|
-
if child.is_a?(Lutaml::Xml::DataModel::XmlElement)
|
|
439
|
-
build_xml_element(inner_xml, child,
|
|
440
|
-
parent_uses_default_ns: this_element_uses_default_ns,
|
|
441
|
-
parent_element_form_default: this_element_form_default,
|
|
442
|
-
parent_namespace_class: element_ns_class)
|
|
443
|
-
elsif child.is_a?(String)
|
|
444
|
-
inner_xml.text(child)
|
|
445
|
-
end
|
|
446
|
-
end
|
|
447
|
-
end
|
|
448
|
-
end
|
|
449
|
-
end
|
|
450
|
-
|
|
451
|
-
# Build XML from XmlDataModel::XmlElement using DeclarationPlan tree (PARALLEL TRAVERSAL)
|
|
452
|
-
#
|
|
453
|
-
# Uses moxml APIs exclusively — no native ::Oga::XML::* classes.
|
|
454
|
-
#
|
|
455
|
-
# @param xml [Builder] XML builder (provides document access)
|
|
456
|
-
# @param xml_element [XmlDataModel::XmlElement] Element content
|
|
457
|
-
# @param plan [DeclarationPlan] Declaration plan with tree structure
|
|
458
|
-
# @param options [Hash] Serialization options
|
|
459
|
-
def build_xml_element_with_plan(xml, xml_element, plan, _options = {})
|
|
460
|
-
moxml_doc = xml.doc
|
|
461
|
-
|
|
462
|
-
root_element = build_moxml_node(xml_element, plan.root_node,
|
|
463
|
-
plan.global_prefix_registry,
|
|
464
|
-
moxml_doc, plan: plan)
|
|
465
|
-
moxml_doc.root = root_element
|
|
466
|
-
|
|
467
|
-
# Add processing instructions before the root element.
|
|
468
|
-
# reverse_each + add_previous_sibling maintains original order.
|
|
469
|
-
xml_element.processing_instructions.reverse_each do |pi|
|
|
470
|
-
pi_node = moxml_doc.create_processing_instruction(pi.target,
|
|
471
|
-
pi.content)
|
|
472
|
-
root_element.add_previous_sibling(pi_node)
|
|
473
|
-
end
|
|
474
|
-
end
|
|
475
|
-
|
|
476
|
-
private
|
|
477
|
-
|
|
478
|
-
# Recursively build moxml element tree using moxml APIs exclusively.
|
|
479
|
-
#
|
|
480
|
-
# @param xml_element [XmlDataModel::XmlElement] Content
|
|
481
|
-
# @param element_node [ElementNode] Decisions
|
|
482
|
-
# @param global_registry [Hash] Global prefix registry (URI => prefix)
|
|
483
|
-
# @param moxml_doc [Moxml::Document] Document for creating nodes
|
|
484
|
-
# @param parent [Moxml::Element, nil] Parent element for xmlns deduplication
|
|
485
|
-
# @param plan [DeclarationPlan] Declaration plan with original namespace URIs
|
|
486
|
-
# @return [Moxml::Element] Created node
|
|
487
|
-
def build_moxml_node(xml_element, element_node, global_registry,
|
|
488
|
-
moxml_doc, parent = nil, plan: nil)
|
|
489
|
-
qualified_name = element_node.qualified_name
|
|
490
|
-
|
|
491
|
-
# 1. Create moxml element
|
|
492
|
-
element = moxml_doc.create_element(qualified_name)
|
|
493
|
-
|
|
494
|
-
# 2. Add hoisted xmlns declarations (filter duplicates from parent)
|
|
495
|
-
original_ns_uris = plan&.original_namespace_uris || {}
|
|
496
|
-
element_node.hoisted_declarations.each do |key, uri|
|
|
497
|
-
next if uri == "http://www.w3.org/XML/1998/namespace"
|
|
498
|
-
|
|
499
|
-
effective_uri = if self.class.fpi?(uri)
|
|
500
|
-
self.class.fpi_to_urn(uri)
|
|
501
|
-
else
|
|
502
|
-
original_ns_uris[uri] || uri
|
|
503
|
-
end
|
|
504
|
-
|
|
505
|
-
prefix = key
|
|
506
|
-
next if parent && parent_has_xmlns_in_chain?(parent, prefix,
|
|
507
|
-
effective_uri)
|
|
508
|
-
|
|
509
|
-
xmlns_name = prefix ? "xmlns:#{prefix}" : "xmlns"
|
|
510
|
-
add_moxml_attribute(element, xmlns_name, effective_uri)
|
|
511
|
-
end
|
|
512
|
-
|
|
513
|
-
# 3. Add regular attributes by INDEX (PARALLEL TRAVERSAL)
|
|
514
|
-
apply_plan_attributes(xml_element, element_node, element)
|
|
515
|
-
|
|
516
|
-
# xsi:nil attribute for W3C compliance
|
|
517
|
-
if xml_element.respond_to?(:xsi_nil) && xml_element.xsi_nil
|
|
518
|
-
add_moxml_attribute(element, "xsi:nil", "true")
|
|
519
|
-
end
|
|
520
|
-
|
|
521
|
-
# schema_location attribute from ElementNode
|
|
522
|
-
element_node.schema_location_attr&.each do |attr_name, attr_value|
|
|
523
|
-
add_moxml_attribute(element, attr_name, attr_value)
|
|
524
|
-
end
|
|
525
|
-
|
|
526
|
-
# W3C Compliance: xmlns="" for blank namespace opt-out
|
|
527
|
-
if element_node.needs_xmlns_blank
|
|
528
|
-
add_moxml_attribute(element, "xmlns", "")
|
|
529
|
-
end
|
|
530
|
-
|
|
531
|
-
# Handle raw content (map_all directive)
|
|
532
|
-
if xml_element.respond_to?(:raw_content)
|
|
533
|
-
raw_content = xml_element.raw_content
|
|
534
|
-
if raw_content && !raw_content.to_s.empty?
|
|
535
|
-
parsed_fragment = moxml_doc.context.parse("<wrapper>#{raw_content}</wrapper>")
|
|
536
|
-
parsed_fragment.root&.children&.each do |child_node|
|
|
537
|
-
element.add_child(child_node)
|
|
538
|
-
end
|
|
539
|
-
return element
|
|
540
|
-
end
|
|
541
|
-
end
|
|
542
|
-
|
|
543
|
-
# 5. Add text content if present
|
|
544
|
-
if xml_element.text_content
|
|
545
|
-
add_content_node(element, xml_element.text_content,
|
|
546
|
-
moxml_doc, cdata: xml_element.cdata)
|
|
547
|
-
end
|
|
548
|
-
|
|
549
|
-
# 6. Recursively build children by INDEX (PARALLEL TRAVERSAL)
|
|
550
|
-
child_element_index = 0
|
|
551
|
-
xml_element.children.each do |xml_child|
|
|
552
|
-
case xml_child
|
|
553
|
-
when Lutaml::Xml::DataModel::XmlElement
|
|
554
|
-
child_node = element_node.element_nodes[child_element_index]
|
|
555
|
-
child_element_index += 1
|
|
556
|
-
|
|
557
|
-
child_element = build_moxml_node(xml_child, child_node,
|
|
558
|
-
global_registry, moxml_doc,
|
|
559
|
-
element, plan: plan)
|
|
560
|
-
element.add_child(child_element)
|
|
561
|
-
when String
|
|
562
|
-
add_content_node(element, xml_child, moxml_doc,
|
|
563
|
-
cdata: xml_element.cdata && !xml_child.strip.empty?)
|
|
564
|
-
when ::Lutaml::Xml::DataModel::XmlComment
|
|
565
|
-
comment_node = moxml_doc.create_comment(xml_child.content)
|
|
566
|
-
element.add_child(comment_node)
|
|
567
|
-
end
|
|
568
|
-
end
|
|
569
|
-
|
|
570
|
-
element
|
|
571
|
-
end
|
|
572
|
-
|
|
573
|
-
def add_moxml_attribute(element, name, value)
|
|
574
|
-
name = name.to_s
|
|
575
|
-
value = value.to_s
|
|
576
|
-
|
|
577
|
-
if name == "xmlns"
|
|
578
|
-
element.add_namespace(nil, value)
|
|
579
|
-
elsif name.start_with?("xmlns:")
|
|
580
|
-
element.add_namespace(name.split(":", 2).last, value)
|
|
581
|
-
else
|
|
582
|
-
element[name] = value
|
|
583
|
-
end
|
|
584
|
-
end
|
|
585
|
-
|
|
586
|
-
# Check if immediate parent element has xmlns declaration
|
|
587
|
-
#
|
|
588
|
-
# @param element [Moxml::Element] parent element to check
|
|
589
|
-
# @param prefix [String, nil] namespace prefix (nil for default namespace)
|
|
590
|
-
# @param uri [String] namespace URI
|
|
591
|
-
# @return [Boolean] true if immediate parent has matching xmlns
|
|
592
|
-
def parent_has_xmlns_in_chain?(element, prefix, uri)
|
|
593
|
-
return false unless element
|
|
594
|
-
|
|
595
|
-
xmlns_name = prefix ? "xmlns:#{prefix}" : "xmlns"
|
|
596
|
-
existing_xmlns = element.attributes.find do |attr|
|
|
597
|
-
attr.name.to_s == xmlns_name && attr.value == uri
|
|
598
|
-
end
|
|
599
|
-
!existing_xmlns.nil?
|
|
600
|
-
end
|
|
601
|
-
|
|
602
|
-
public
|
|
603
|
-
|
|
604
|
-
# NOTE: build_unordered_children_with_plan and build_ordered_element_with_plan
|
|
605
|
-
# are inherited from BaseAdapter - no need to override
|
|
606
|
-
|
|
607
|
-
# Handle nested model elements with plan
|
|
608
|
-
def handle_nested_elements_with_plan(xml, value, rule, attribute, plan,
|
|
609
|
-
options, parent_plan: nil)
|
|
610
|
-
element_options = options.merge(
|
|
611
|
-
rule: rule,
|
|
612
|
-
attribute: attribute,
|
|
613
|
-
tag_name: rule.name,
|
|
614
|
-
mapper_class: attribute.type(register), # Override with child's type
|
|
615
|
-
)
|
|
616
|
-
|
|
617
|
-
if value.is_a?(Lutaml::Model::Collection)
|
|
618
|
-
items = value.collection
|
|
619
|
-
attr_type = attribute.type(register)
|
|
620
|
-
|
|
621
|
-
if attr_type <= Lutaml::Model::Type::Value
|
|
622
|
-
# Simple types - use add_simple_value for each item
|
|
623
|
-
items.each do |val|
|
|
624
|
-
xml_mapping = options[:mapper_class]&.mappings_for(:xml)
|
|
625
|
-
add_simple_value(xml, rule, val, attribute, plan: parent_plan,
|
|
626
|
-
mapping: xml_mapping, options: options)
|
|
627
|
-
end
|
|
628
|
-
else
|
|
629
|
-
# Model types - build elements with plans
|
|
630
|
-
items.each do |val|
|
|
631
|
-
# For polymorphic collections, use each item's actual class
|
|
632
|
-
item_mapper_class = if polymorphic_value?(attribute, val)
|
|
633
|
-
val.class
|
|
634
|
-
else
|
|
635
|
-
attribute.type(register)
|
|
636
|
-
end
|
|
637
|
-
|
|
638
|
-
# CRITICAL: Collect and plan for each item individually
|
|
639
|
-
item_mapping = item_mapper_class.mappings_for(:xml)
|
|
640
|
-
if item_mapping
|
|
641
|
-
collector = NamespaceCollector.new(register)
|
|
642
|
-
item_needs = collector.collect(val, item_mapping)
|
|
643
|
-
|
|
644
|
-
planner = DeclarationPlanner.new(register)
|
|
645
|
-
item_plan = planner.plan(val, item_mapping, item_needs,
|
|
646
|
-
parent_plan: parent_plan, options: options)
|
|
647
|
-
else
|
|
648
|
-
item_plan = plan
|
|
649
|
-
end
|
|
650
|
-
|
|
651
|
-
item_options = element_options.merge(mapper_class: item_mapper_class)
|
|
652
|
-
build_element_with_plan(xml, val, item_plan, item_options)
|
|
653
|
-
end
|
|
654
|
-
end
|
|
655
|
-
return
|
|
656
|
-
end
|
|
657
|
-
|
|
658
|
-
case value
|
|
659
|
-
when Array
|
|
660
|
-
value.each do |val|
|
|
661
|
-
# For polymorphic arrays, use each item's actual class
|
|
662
|
-
item_mapper_class = if polymorphic_value?(attribute, val)
|
|
663
|
-
val.class
|
|
664
|
-
else
|
|
665
|
-
attribute.type(register)
|
|
666
|
-
end
|
|
667
|
-
|
|
668
|
-
# CRITICAL: Collect and plan for each array item individually
|
|
669
|
-
item_mapping = item_mapper_class.mappings_for(:xml)
|
|
670
|
-
if item_mapping
|
|
671
|
-
collector = NamespaceCollector.new(register)
|
|
672
|
-
item_needs = collector.collect(val, item_mapping)
|
|
673
|
-
|
|
674
|
-
planner = DeclarationPlanner.new(register)
|
|
675
|
-
item_plan = planner.plan(val, item_mapping, item_needs,
|
|
676
|
-
parent_plan: parent_plan, options: options)
|
|
677
|
-
else
|
|
678
|
-
item_plan = plan
|
|
679
|
-
end
|
|
680
|
-
|
|
681
|
-
item_options = element_options.merge(mapper_class: item_mapper_class)
|
|
682
|
-
if item_plan
|
|
683
|
-
build_element_with_plan(xml, val, item_plan, item_options)
|
|
684
|
-
else
|
|
685
|
-
build_element(xml, val, item_options)
|
|
686
|
-
end
|
|
687
|
-
end
|
|
688
|
-
else
|
|
689
|
-
build_element_with_plan(xml, value, plan, element_options)
|
|
690
|
-
end
|
|
691
|
-
end
|
|
692
|
-
|
|
693
|
-
# Add simple (non-model) values to XML
|
|
694
|
-
def add_simple_value(xml, rule, value, attribute, plan: nil,
|
|
695
|
-
mapping: nil)
|
|
696
|
-
# Handle array values by creating multiple elements
|
|
697
|
-
if value.is_a?(Array)
|
|
698
|
-
value.each do |val|
|
|
699
|
-
add_simple_value(xml, rule, val, attribute, plan: plan,
|
|
700
|
-
mapping: mapping)
|
|
701
|
-
end
|
|
702
|
-
return
|
|
703
|
-
end
|
|
704
|
-
|
|
705
|
-
# Determine prefix for this element based on namespace rules
|
|
706
|
-
# Initialize namespace resolver
|
|
707
|
-
resolver = NamespaceResolver.new(register)
|
|
708
|
-
|
|
709
|
-
# Extract parent_uses_default_ns from options or calculate it
|
|
710
|
-
parent_uses_default_ns = options[:parent_uses_default_ns]
|
|
711
|
-
if parent_uses_default_ns.nil?
|
|
712
|
-
parent_uses_default_ns = if mapping&.namespace_class && plan
|
|
713
|
-
key = mapping.namespace_class.to_key
|
|
714
|
-
ns_decl = plan.namespace(key)
|
|
715
|
-
ns_decl&.declared_here? && ns_decl.default_format?
|
|
716
|
-
else
|
|
717
|
-
false
|
|
718
|
-
end
|
|
719
|
-
end
|
|
720
|
-
|
|
721
|
-
# Resolve namespace using the resolver
|
|
722
|
-
ns_result = resolver.resolve_for_element(rule, attribute, mapping,
|
|
723
|
-
plan, options)
|
|
724
|
-
ns_result[:prefix]
|
|
725
|
-
type_ns_info = ns_result[:ns_info]
|
|
726
|
-
|
|
727
|
-
# BUG FIX #49: Check if child element is in same namespace as parent
|
|
728
|
-
# If yes, inherit parent's format (default vs prefix)
|
|
729
|
-
|
|
730
|
-
# Get parent's namespace URI
|
|
731
|
-
parent_ns_class = options[:parent_namespace_class]
|
|
732
|
-
parent_ns_decl = options[:parent_ns_decl]
|
|
733
|
-
parent_ns_uri = parent_ns_class&.uri
|
|
734
|
-
|
|
735
|
-
# Get child's resolved namespace URI
|
|
736
|
-
child_ns_uri = ns_result[:uri]
|
|
737
|
-
|
|
738
|
-
# Initialize resolved_prefix from namespace resolution
|
|
739
|
-
resolved_prefix = ns_result[:prefix]
|
|
740
|
-
|
|
741
|
-
# CRITICAL FIX FOR NATIVE TYPE NAMESPACE INHERITANCE:
|
|
742
|
-
# Elements without explicit namespace declaration should NOT inherit
|
|
743
|
-
# parent's prefix format. They should be in blank namespace.
|
|
744
|
-
#
|
|
745
|
-
# Check if this is a native type without explicit namespace:
|
|
746
|
-
# 1. No namespace directive on the mapping rule
|
|
747
|
-
# 2. Attribute type doesn't have namespace_class (native type like :string)
|
|
748
|
-
element_has_no_explicit_ns = !rule.namespace_set?
|
|
749
|
-
type_class = attribute&.type(register)
|
|
750
|
-
type_has_no_ns = !(type_class.is_a?(Class) && type_class <= Lutaml::Model::Type::Value) ||
|
|
751
|
-
!type_class&.namespace_class
|
|
752
|
-
|
|
753
|
-
# If native type with no explicit namespace, DON'T inherit parent's prefix
|
|
754
|
-
if element_has_no_explicit_ns && type_has_no_ns
|
|
755
|
-
# Native type - force blank namespace (no prefix)
|
|
756
|
-
resolved_prefix = nil
|
|
757
|
-
# Check if parent uses default format - if so, need xmlns="" to opt out
|
|
758
|
-
parent_ns_decl&.default_format?
|
|
759
|
-
# Only inherit format if child is in SAME namespace as parent (matching URIs)
|
|
760
|
-
elsif parent_ns_class && parent_ns_decl &&
|
|
761
|
-
child_ns_uri && parent_ns_uri &&
|
|
762
|
-
child_ns_uri == parent_ns_uri
|
|
763
|
-
# Same namespace URI - inherit parent's format
|
|
764
|
-
resolved_prefix = if parent_ns_decl.prefix_format?
|
|
765
|
-
parent_ns_decl.prefix
|
|
766
|
-
else
|
|
767
|
-
# Parent uses default format, child should too (no prefix)
|
|
768
|
-
nil
|
|
769
|
-
end
|
|
770
|
-
end
|
|
771
|
-
|
|
772
|
-
# Prepare attributes with xmlns if needed
|
|
773
|
-
attributes = {}
|
|
774
|
-
|
|
775
|
-
# W3C COMPLIANCE: Use resolver to determine xmlns="" requirement
|
|
776
|
-
if resolver.xmlns_blank_required?(ns_result, parent_uses_default_ns)
|
|
777
|
-
attributes["xmlns"] = ""
|
|
778
|
-
end
|
|
779
|
-
|
|
780
|
-
# Check if this namespace needs local declaration (out of scope)
|
|
781
|
-
if resolved_prefix && plan&.namespaces
|
|
782
|
-
ns_entry = plan.namespaces.values.find do |ns_decl|
|
|
783
|
-
ns_decl.ns_object.prefix_default == resolved_prefix ||
|
|
784
|
-
(type_ns_info && type_ns_info[:uri] && ns_decl.ns_object.uri == type_ns_info[:uri])
|
|
785
|
-
end
|
|
786
|
-
|
|
787
|
-
if ns_entry&.local_on_use?
|
|
788
|
-
xmlns_attr = resolved_prefix ? "xmlns:#{resolved_prefix}" : "xmlns"
|
|
789
|
-
attributes[xmlns_attr] = ns_entry.ns_object.uri
|
|
790
|
-
end
|
|
791
|
-
end
|
|
792
|
-
|
|
793
|
-
if value.nil?
|
|
794
|
-
xml.create_and_add_element(rule.name,
|
|
795
|
-
attributes: attributes.merge({ "xsi:nil" => true }),
|
|
796
|
-
prefix: resolved_prefix)
|
|
797
|
-
elsif ::Lutaml::Model::Utils.empty?(value)
|
|
798
|
-
xml.create_and_add_element(rule.name,
|
|
799
|
-
attributes: attributes.empty? ? nil : attributes,
|
|
800
|
-
prefix: resolved_prefix)
|
|
801
|
-
elsif rule.raw_mapping?
|
|
802
|
-
xml.add_xml_fragment(xml, value)
|
|
803
|
-
else
|
|
804
|
-
xml.create_and_add_element(rule.name,
|
|
805
|
-
attributes: attributes.empty? ? nil : attributes,
|
|
806
|
-
prefix: resolved_prefix) do
|
|
807
|
-
add_value(xml, value, attribute, cdata: rule.cdata)
|
|
808
|
-
end
|
|
809
|
-
end
|
|
810
|
-
end
|
|
811
|
-
|
|
812
|
-
def attributes_hash(element)
|
|
813
|
-
result = Lutaml::Model::MappingHash.new
|
|
814
|
-
|
|
815
|
-
element.attributes_each_value do |attr|
|
|
816
|
-
if attr.name == "schemaLocation"
|
|
817
|
-
result["__schema_location"] = {
|
|
818
|
-
namespace: attr.namespace,
|
|
819
|
-
prefix: attr.namespace.prefix,
|
|
820
|
-
schema_location: attr.value,
|
|
821
|
-
}
|
|
822
|
-
else
|
|
823
|
-
result[self.class.namespaced_attr_name(attr)] = attr.value
|
|
824
|
-
end
|
|
825
|
-
end
|
|
826
|
-
|
|
827
|
-
result
|
|
828
|
-
end
|
|
829
|
-
|
|
830
|
-
# NOTE: name_of, prefixed_name_of, namespaced_attr_name, namespaced_name_of
|
|
831
|
-
# are provided by AdapterHelpers module via extend
|
|
832
|
-
|
|
833
|
-
def self.text_of(element)
|
|
834
|
-
element.text
|
|
835
|
-
end
|
|
836
|
-
|
|
837
|
-
def order
|
|
838
|
-
children.filter_map do |child|
|
|
839
|
-
if child.text?
|
|
840
|
-
next if child.text.nil?
|
|
841
|
-
|
|
842
|
-
Element.new("Text", "text", text_content: child.text)
|
|
843
|
-
elsif child.comment?
|
|
844
|
-
Element.new("Comment", "comment",
|
|
845
|
-
text_content: child.content,
|
|
846
|
-
node_type: :comment)
|
|
847
|
-
else
|
|
848
|
-
Element.new("Element", child.unprefixed_name)
|
|
849
|
-
end
|
|
850
|
-
end
|
|
851
|
-
end
|
|
852
|
-
|
|
853
|
-
# order_of is inherited from BaseAdapter (delegates to element.order)
|
|
11
|
+
MOXML_ADAPTER = Moxml::Adapter::Oga
|
|
12
|
+
BUILDER_CLASS = Builder::Oga
|
|
13
|
+
PARSED_ELEMENT_CLASS = Oga::Element
|
|
854
14
|
end
|
|
855
15
|
end
|
|
856
16
|
end
|