Class: Lutaml::Xml::Transformation

Inherits:
Model::Transformation
  • Object
show all
Includes:
Lutaml::Xml::TransformationSupport::OrderReconciler, Lutaml::Xml::TransformationSupport::OrderedApplier, Lutaml::Xml::TransformationSupport::RuleApplier, Lutaml::Xml::TransformationSupport::RuleCompiler
Defined in:
lib/lutaml/xml/transformation.rb

Overview

XML-specific transformation implementation.

Transforms model instances into XmlElement trees without triggering type resolution or imports during transformation.

This class orchestrates the transformation process by including specialized modules for each concern:

  • TransformationSupport::RuleCompiler: Compiles mapping DSL to CompiledRule objects
  • TransformationSupport::SkipLogic: Determines if values should be skipped
  • TransformationSupport::ValueSerializer: Serializes values to XML strings
  • TransformationSupport::ElementBuilder: Creates XML elements from values
  • TransformationSupport::OrderedApplier: Applies rules in element order for round-trip
  • TransformationSupport::OrderReconciler: Aligns element_order with current values
  • TransformationSupport::RuleApplier: Dispatches rule application to handlers

Instance Method Summary collapse

Methods included from Lutaml::Xml::TransformationSupport::OrderReconciler

#reconciled_element_order

Methods included from Lutaml::Xml::TransformationSupport::OrderedApplier

#apply_element_rule_single, #apply_rules_in_order, #find_rule_for_element

Methods included from Lutaml::Xml::TransformationSupport::RuleApplier

#apply_attribute_rule, #apply_content_rule, #apply_element_rule, #apply_raw_rule, #apply_rule

Methods included from Lutaml::Xml::TransformationSupport::ElementBuilder

#add_hash_children, #apply_element_content, #apply_nil_marker, #apply_nil_marker_for_empty, #apply_nil_marker_for_nil, #apply_nil_marker_for_uninitialized, #apply_unqualified_form_default, #create_fallback_nested_element, #create_nested_collection_wrapper_element, #create_nested_model_element, #create_simple_value_element, #create_transformed_nested_element, #determine_element_namespace, #namespace_prefix_valid_for_class, #propagate_form, #resolve_polymorphic_class

Methods included from Lutaml::Xml::TransformationSupport::SkipLogic

#should_skip_delegated_value?, #should_skip_value?

Methods included from Model::RenderPolicy

derived_attribute_for?, #should_skip_delegated_value?, #should_skip_value?

Methods included from Lutaml::Xml::TransformationSupport::RuleCompiler

#build_value_transformer, #compile_attribute_rule, #compile_content_rule, #compile_element_rule, #compile_raw_rule

Instance Method Details

#all_namespacesArray<Class>

Collect all namespaces used in this transformation

Returns:

  • Array of XmlNamespace classes



109
110
111
112
113
114
115
# File 'lib/lutaml/xml/transformation.rb', line 109

def all_namespaces
  namespaces = []
  compiled_rules.each do |rule|
    namespaces.concat(rule.all_namespaces)
  end
  namespaces.uniq
end

#transform(model_instance, options = {}) ⇒ ::Lutaml::Xml::DataModel::XmlElement

Transform a model instance into XmlElement tree

Parameters:

  • The model instance to transform

  • (defaults to: {})

    Transformation options

Returns:

  • The root XML element



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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/lutaml/xml/transformation.rb', line 30

def transform(model_instance, options = {})
  # Get root element name from mapping
  mapping = model_class.mappings_for(:xml, register_id)
  root_name = options[:tag_name] ||
    mapping.root_element ||
    (model_class.name&.split("::")&.last || "anonymous")

  # Get root namespace
  root_namespace = mapping.namespace_class

  # Check if this root element needs xmlns="" due to parent context
  parent_uses_default_ns = options[:parent_uses_default_ns]
  child_explicitly_blank = mapping.namespace_param == :blank
  needs_xmlns_blank = parent_uses_default_ns && child_explicitly_blank

  # Create root element
  root = ::Lutaml::Xml::DataModel::XmlElement.new(root_name,
                                                  root_namespace)

  # Preserve original namespace prefix for doubly-defined namespace support.
  # The model instance carries @__xml_namespace_prefix from deserialization.
  # ONLY set on root XmlElement when:
  # (a) This is the root of the to_xml call (no parent) OR
  # (b) The child's namespace matches the parent's namespace (same namespace).
  # DO NOT set when child's namespace differs from parent's - the FormatPreservationRule
  # will correctly determine the format based on input_prefix_formats.
  if model_instance.is_a?(::Lutaml::Model::Serialize)
    ns_prefix = model_instance.xml_namespace_prefix
    if ns_prefix && !ns_prefix.empty?
      # Only set if root of to_xml call OR namespaces match
      parent_ns_class = options[:parent_namespace_class]
      if parent_ns_class.nil? || parent_ns_class == root_namespace
        root.xml_namespace_prefix = ns_prefix
      end
    end

    # Preserve original namespace URI for namespace alias support.
    # When the model's namespace URI differs from the canonical URI (it's an alias),
    # transfer this information to the XmlElement so it can be used during
    # serialization for round-trip fidelity.
    original_ns_uri = model_instance.original_namespace_uri
    if original_ns_uri && !original_ns_uri.empty?
      root.original_namespace_uri = original_ns_uri
    end
  end

  # Mark that this element needs xmlns="" (for DeclarationPlanner)
  if needs_xmlns_blank
    root.needs_xmlns_blank = true
  end

  # Store namespace_scope_config for hoisting support
  namespace_scope_config = mapping.namespace_scope_config || []
  root.namespace_scope_config = namespace_scope_config

  # Handle schema_location if present
  handle_schema_location(root, model_instance)

  # Handle xml:space="preserve" for mixed content elements
  handle_xml_space(root, mapping)

  # Handle processing instruction mappings
  apply_processing_instruction_mappings(root, model_instance, mapping)

  # Determine serialization mode
  use_element_order = should_use_element_order?(model_instance, mapping)

  if use_element_order
    apply_ordered_rules(root, model_instance, options)
  else
    apply_standard_rules(root, model_instance, options)
  end

  root
end