Class: Canon::Diff::NodeSerializer

Inherits:
Object
  • Object
show all
Defined in:
lib/canon/diff/node_serializer.rb

Overview

Serializes nodes from different parsing libraries into canonical strings This abstraction allows Canon to work with any parsing library (Nokogiri, Moxml, etc.) without being tied to a specific implementation.

This is library-agnostic because it detects node type and uses the appropriate serialization method.

Class Method Summary collapse

Class Method Details

.element_name(node) ⇒ String

Get element name from a node Handles both Nokogiri and Canon nodes

Parameters:

  • node (Object)

    Node to get name from

Returns:

  • (String)

    Element name



135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/canon/diff/node_serializer.rb', line 135

def self.element_name(node)
  return "" if node.nil?

  # Handle Canon::Xml::Nodes::ElementNode
  if node.is_a?(Canon::Xml::Nodes::ElementNode)
    return node.name
  end

  # Handle Nokogiri/moxml elements
  name = Canon::XmlParsing.name(node)
  return name.to_s if name

  ""
end

.extract_attributes(node) ⇒ Hash

Extract attributes from a node as a normalized hash Handles both Nokogiri and Canon nodes

Parameters:

  • node (Object)

    Node to extract attributes from

Returns:

  • (Hash)

    Normalized attributes hash



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/canon/diff/node_serializer.rb', line 96

def self.extract_attributes(node)
  return {} if node.nil?

  # Handle Canon::Xml::Nodes::ElementNode
  if node.is_a?(Canon::Xml::Nodes::ElementNode)
    attrs = {}
    node.attribute_nodes.each do |attr|
      attrs[attr.name] = attr.value
    end
    return attrs
  end

  # Handle Nokogiri/moxml elements via XmlParsing
  if Canon::XmlParsing.element?(node)
    attrs = {}
    Canon::XmlParsing.attributes(node).each do |attr|
      attrs[attr.name] = attr.value
    end
    return attrs
  end

  # Handle other elements with attributes method
  if node.is_a?(Canon::Xml::Node)
    return {}
  end

  # Handle TreeNode attributes (already a hash)
  if node.is_a?(Hash)
    return node
  end

  {}
end

.serialize(node) ⇒ String

Serialize a node to a string for display Handles both Nokogiri and Canon nodes

Parameters:

  • node (Object)

    Node to serialize (Nokogiri, Canon, or nil)

Returns:

  • (String)

    Serialized string representation



24
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
# File 'lib/canon/diff/node_serializer.rb', line 24

def self.serialize(node)
  return "" if node.nil?

  # Handle Canon::Xml::Nodes::TextNode
  if node.is_a?(Canon::Xml::Nodes::TextNode)
    # Use original text (with entity references) if available,
    # otherwise fall back to value (decoded text)
    return node.original || node.value
  end

  # Handle Canon::Xml::Nodes::CommentNode
  if node.is_a?(Canon::Xml::Nodes::CommentNode)
    return "<!--#{node.value}-->"
  end

  # Handle Canon::Xml::Nodes::ElementNode
  if node.is_a?(Canon::Xml::Nodes::ElementNode)
    return serialize_element_node(node)
  end

  # Handle Canon::Xml::Nodes::ProcessingInstructionNode
  if node.is_a?(Canon::Xml::Nodes::ProcessingInstructionNode)
    return "<?#{node.target} #{node.data}?>"
  end

  # Handle Canon::Xml::Nodes::RootNode - serialize children
  if node.is_a?(Canon::Xml::Nodes::RootNode)
    return node.children.map { |child| serialize(child) }.join
  end

  # Handle Nokogiri/moxml nodes
  if Canon::XmlParsing.xml_node?(node)
    return Canon::XmlParsing.serialize(node)
  end

  # Handle tree diff nodes and other objects with serialization
  if node.is_a?(Canon::TreeDiff::Core::TreeNode)
    return serialize_treenode(node)
  end

  node.to_s
end

.serialize_attributes(attributes) ⇒ String

Serialize attributes to string format Returns attributes in “ name="value"” format

Parameters:

  • attributes (Hash)

    Attributes hash

Returns:

  • (String)

    Serialized attributes



177
178
179
180
181
182
183
# File 'lib/canon/diff/node_serializer.rb', line 177

def self.serialize_attributes(attributes)
  return "" if attributes.nil? || attributes.empty?

  attributes.sort.map do |name, value|
    " #{name}=\"#{value}\""
  end.join
end

.serialize_element_node(element) ⇒ String

Serialize an ElementNode to HTML/XML string

Parameters:

Returns:

  • (String)

    Serialized element



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/canon/diff/node_serializer.rb', line 71

def self.serialize_element_node(element)
  # Build opening tag with attributes
  tag = "<#{element.name}"

  # Add attributes
  element.sorted_attribute_nodes.each do |attr|
    tag += " #{attr.name}=\"#{attr.value}\""
  end

  # Check if element has children
  if element.children.empty?
    # Self-closing tag for empty elements
    "#{tag}/>"
  else
    # Full element with children
    content = element.children.map { |child| serialize(child) }.join
    "#{tag}>#{content}</#{element.name}>"
  end
end

.text_content(node) ⇒ String

Get text content from a node Handles both Nokogiri and Canon nodes

Parameters:

  • node (Object)

    Node to get text from

Returns:

  • (String)

    Text content



155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/canon/diff/node_serializer.rb', line 155

def self.text_content(node)
  return "" if node.nil?

  # Handle Canon::Xml::Nodes::TextNode
  if node.is_a?(Canon::Xml::Nodes::TextNode)
    return node.value.to_s
  end

  # Handle Canon::Xml::Node
  if node.is_a?(Canon::Xml::Node)
    return node.text_content.to_s
  end

  # Handle Nokogiri/moxml nodes
  Canon::XmlParsing.text_content(node).to_s
end