Class: Lutaml::Xml::Transformation

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::RuleApplier: Dispatches rule application to handlers

Instance Attribute Summary

Attributes inherited from Model::Transformation

#compiled_rules, #format, #model_class, #register

Instance Method Summary collapse

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

#determine_element_namespace

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

Methods inherited from Model::Transformation

#initialize

Constructor Details

This class inherits a constructor from Lutaml::Model::Transformation

Instance Method Details

#all_namespacesArray<Class>

Collect all namespaces used in this transformation

Returns:

  • (Array<Class>)

    Array of XmlNamespace classes



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

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:

  • model_instance (Object)

    The model instance to transform

  • options (Hash) (defaults to: {})

    Transformation options

Returns:



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
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
# File 'lib/lutaml/xml/transformation.rb', line 28

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