To solve the problem where you have two functionally equivalent XML files, but they fail comparatively because their elements are ordered differently, I've created the following function.
def sort_child_elements(doc)
doc.each_recursive() do |node|
s_elements = node.elements.sort_by {|elt|
if elt.has_attributes?
a_str = ""
attr = elt.attributes
attr.each_attribute { |a|
a_str = "#{a_str}_#{a.to_s}"
}
"#{elt.name}#{a_str}"
else
"#{elt.name}"
end
}
o_elements = node.elements.to_a
o_elements.each_index() {|i|
node.elements.delete(o_elements[i])
}
s_elements.each_index() {|i|
node.elements.add(s_elements[i])
}
end
return doc
end
This function takes a REXML::Document, sorts the elements ascii-betically, and returns the sorted REXML::Document.
Below is a full example of using this with Test::Unit::XML.
require File.dirname(__FILE__) + '/../test_helper'
require 'rexml/document'
require 'test/unit/xml'
class TestTestUnitXml < Test::Unit::TestCase
def setup
reference_xml = <<-EOS
<service>
<obj id='1'>
<foo>bar</foo>
<zot>woot</zot>
<subobj id='1'>
<hey>man1</hey>
<test>here1</test>
</subobj>
<subobj id='2'>
<hey>man2</hey>
<test>here2</test>
</subobj>
</obj>
<obj id='2'>
<foo>bar</foo>
<zot>woot</zot>
<subobj id='1'>
<hey>man1</hey>
<test>here1</test>
</subobj>
<subobj id='2'>
<hey>man2</hey>
<test>here2</test>
</subobj>
</obj>
</service>
EOS
test_xml = <<-EOS
<service>
<obj id='2'>
<zot>woot</zot>
<foo>bar</foo>
<subobj id='1'>
<hey>man1</hey>
<test>here1</test>
</subobj>
<subobj id='2'>
<hey>man2</hey>
<test>here2</test>
</subobj>
</obj>
<obj id='1'>
<foo>bar</foo>
<zot>woot</zot>
<subobj id='1'>
<hey>man1</hey>
<test>here1</test>
</subobj>
<subobj id='2'>
<hey>man2</hey>
<test>here2</test>
</subobj>
</obj>
</service>
EOS
@expected = REXML::Document.new(reference_xml.gsub(/\t/, '').gsub(/\n/, ''))
@actual = REXML::Document.new(test_xml.gsub(/\t/, '').gsub(/\n/, ''))
end
def sort_child_elements(doc)
doc.each_recursive() do |node|
s_elements = node.elements.sort_by {|elt|
if elt.has_attributes?
a_str = ""
attr = elt.attributes
attr.each_attribute { |a|
a_str = "#{a_str}_#{a.to_s}"
}
"#{elt.name}#{a_str}"
else
"#{elt.name}"
end
}
o_elements = node.elements.to_a
o_elements.each_index() {|i|
node.elements.delete(o_elements[i])
}
s_elements.each_index() {|i|
node.elements.add(s_elements[i])
}
end
return doc
end
def test_sorted_xml
assert_xml_equal(sort_child_elements(@expected), sort_child_elements(@actual))
end
def test_original_xml
assert_xml_equal(@expected, @actual)
end
end
|