class Canon::Comparison::XmlComparatorHelpers::NodeParser

Handles conversion of strings and various node types to Canon::Xml::Node
Node parser with preprocessing support

def self.apply_preprocessing(xml_string, preprocessing)

Returns:
  • (String) - Preprocessed XML string

Parameters:
  • preprocessing (Symbol) -- Preprocessing mode
  • xml_string (String) -- XML string to preprocess
def self.apply_preprocessing(xml_string, preprocessing)
  case preprocessing
  when :normalize
    # Normalize whitespace: collapse runs, trim lines
    xml_string.lines.map(&:strip).reject(&:empty?).join("\n")
  when :c14n
    # Canonicalize the XML
    Canon::Xml::C14n.canonicalize(xml_string, with_comments: false)
  when :format
    # Pretty format the XML
    Canon.format(xml_string, :xml)
  else
    # :none or unrecognized - use as-is
    xml_string
  end
end

def self.convert_from_node(node, preserve_whitespace: false,

Returns:
  • (Canon::Xml::Node) - Converted node

Parameters:
  • parser (Symbol, nil) -- Parser backend override
  • preserve_whitespace (Boolean) -- Whether to preserve whitespace-only text nodes
  • node (Object) -- Nokogiri or Moxml node
def self.convert_from_node(node, preserve_whitespace: false,
nil)
  if Canon::XmlBackend.nokogiri?
    if node.is_a?(Nokogiri::XML::Node)
      return Canon::Xml::DataModel.build_from_nokogiri(
        node, preserve_whitespace: preserve_whitespace
      )
    end
  elsif node.is_a?(Moxml::Node)
    return Canon::Xml::DataModel.build_from_moxml(
      node, preserve_whitespace: preserve_whitespace
    )
  end
  xml_str = if node.is_a?(String)
              node
            else
              node.to_xml
            end
  resolved_parser = parser || resolve_parser_config
  if resolved_parser == :sax && RUBY_ENGINE != "opal"
    Canon::Xml::SaxBuilder.parse(xml_str,
                                 preserve_whitespace: preserve_whitespace)
  else
    Canon::Xml::DataModel.from_xml(xml_str,
                                   preserve_whitespace: preserve_whitespace)
  end
end

def self.parse(node, preprocessing = :none, preserve_whitespace: false,

Returns:
  • (Canon::Xml::Node) - Parsed node

Parameters:
  • parser (Symbol) -- Parser backend (:sax or :dom, default from config)
  • preserve_whitespace (Boolean) -- Whether to preserve whitespace-only text nodes
  • preprocessing (Symbol) -- Preprocessing mode (:none, :normalize, :c14n, :format)
  • node (String, Object) -- Node to parse
def self.parse(node, preprocessing = :none, preserve_whitespace: false,
               parser: nil)
  # If already a Canon::Xml::Node, return as-is
  return node if node.is_a?(Canon::Xml::Node)
  # If it's a Nokogiri or Moxml node, convert to DataModel
  unless node.is_a?(String)
    return convert_from_node(node,
                             preserve_whitespace: preserve_whitespace,
                             parser: parser)
  end
  # Normalize encoding before preprocessing (UTF-16 strings can't use strip, etc.)
  node = Canon::Xml::DataModel.normalize_encoding(node)
  # Apply preprocessing to XML string before parsing
  xml_string = apply_preprocessing(node, preprocessing).strip
  # Select parser backend
  resolved_parser = parser || resolve_parser_config
  if resolved_parser == :sax && RUBY_ENGINE != "opal"
    Canon::Xml::SaxBuilder.parse(xml_string,
                                 preserve_whitespace: preserve_whitespace)
  else
    Canon::Xml::DataModel.from_xml(xml_string,
                                   preserve_whitespace: preserve_whitespace)
  end
end

def self.resolve_parser_config

Returns:
  • (Symbol) - :sax or :dom
def self.resolve_parser_config
  Canon::Config.instance.xml.diff.parser
rescue StandardError
  Canon::XmlBackend.nokogiri? ? :sax : :dom
end