Module: Lutaml::Xml::TransformationSupport::OrderedApplier
- Included in:
- Lutaml::Xml::Transformation
- Defined in:
- lib/lutaml/xml/transformation/ordered_applier.rb
Overview
Module for applying rules in element order for round-trip preservation.
When element_order was captured during parsing (model was deserialized from XML) AND the mapping is marked as ordered, this module ensures the original XML structure is preserved during serialization.
Instance Method Summary collapse
-
#apply_element_rule_single(parent:, rule:, value:, options:) { ... } ⇒ Object
Apply element rule for a single value from a collection.
-
#apply_rules_in_order(root, model_instance, options, compiled_rules, model_class, register_id) { ... } ⇒ Object
Apply rules in the order specified by element_order.
-
#find_rule_for_element(object, compiled_rules) ⇒ CompiledRule?
Find the mapping rule for an element from element_order.
Instance Method Details
#apply_element_rule_single(parent:, rule:, value:, options:) { ... } ⇒ Object
Apply element rule for a single value from a collection
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/lutaml/xml/transformation/ordered_applier.rb', line 83 def apply_element_rule_single(parent:, rule:, value:, options:) # Extract parent's namespace info for element_form_default inheritance parent_ns_class = parent.namespace_class # Only pass element_form_default VALUE if it was explicitly set # When not set (defaults to :unqualified), pass nil to avoid incorrect blank namespace treatment parent_element_form_default = if parent_ns_class&.element_form_default_set? parent_ns_class.element_form_default end # Merge parent context into options = .merge( parent_namespace_class: parent_ns_class, parent_element_form_default: parent_element_form_default, parent_element: parent, ) element = yield(rule, value, ) parent.add_child(element) if element end |
#apply_rules_in_order(root, model_instance, options, compiled_rules, model_class, register_id) { ... } ⇒ Object
Apply rules in the order specified by element_order
This ensures round-trip serialization preserves the original XML structure. For mixed content, text nodes from element_order are added directly to preserve the original text interleaving.
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/lutaml/xml/transformation/ordered_applier.rb', line 25 def apply_rules_in_order(root, model_instance, , compiled_rules, model_class, register_id) element_order = model_instance.element_order mapping = model_class.mappings_for(:xml, register_id) # Track index per element type for collection attributes element_indices = ::Hash.new(0) # Track text node index for content-mapped attribute access. text_node_index = 0 # Track whether we processed any text nodes from element_order processed_text_nodes = false # Find the content rule to get CDATA flag for mixed content text content_rule = compiled_rules.find do |r| r.option(:mapping_type) == :content end root.cdata = true if content_rule&.cdata # Pre-check: can we use the content attribute for indexed text access? # This requires the content array length to match the text node count # in element_order. When they match, mutations to the content array # are reflected in serialization. When they don't match (e.g., CDATA # creates extra whitespace text nodes), we fall back to element_order. text_node_count = element_order.count { |o| o.type == "Text" } content_value = content_rule && model_instance&.public_send(content_rule.attribute_name) use_content_index = content_rule && content_value.is_a?(Array) && content_value.length == text_node_count # Iterate through element_order to preserve original sequence element_order.each do |object| result = process_element_order_item( object, root, model_instance, , compiled_rules, mapping, element_indices, content_rule, text_node_index, text_node_count, use_content_index ) do |action, rule, value, xsi_nil_flag| yield(action, rule, value, xsi_nil_flag) if block_given? end text_node_index += 1 if object.type == "Text" processed_text_nodes = true if result == :text_node end # Apply remaining rules that weren't in element_order (attributes only) apply_remaining_rules(root, model_instance, , compiled_rules, mapping, processed_text_nodes) do |action, rule, value| yield(action, rule, value) if block_given? end end |
#find_rule_for_element(object, compiled_rules) ⇒ CompiledRule?
Find the mapping rule for an element from element_order
108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/lutaml/xml/transformation/ordered_applier.rb', line 108 def find_rule_for_element(object, compiled_rules) return nil unless object.type == "Element" object_ns_uri = object.namespace_uri # nil if old element_order (backward compat) compiled_rules.find do |r| r.is_a?(::Lutaml::Model::CompiledRule) && r.option(:mapping_type) == :element && matches_element_rule?(r, object.name, object_ns_uri) end end |