Rails to_xml - arrays with non-ActiveRecord objects

Posted on December 29, 2007

If you've got an array that contains objects with nested class names like this:

module Foo
  class Bar
    def to_xml options = {} # :nodoc:
      builder = options[:builder] || Builder::XmlMarkup.new(options)
      builder.some_tag "some value"
    end
  end
end

def test_array_with_to_xml
  f = Foo::Bar.new
  puts [f].to_xml
end

test_array_with_to_xml

You'll end up with invalid xml:

<?xml version="1.0" encoding="UTF-8"?>
<foo/bars type="array">
  <some_tag>some value</some_tag>
</foo/bars>

That's because the array #to_xml (it's in /usr/local/lib/ruby/gems/1.8/gems/activesupport-2.0.2/lib/activesupport/coreext/array/conversions.rb on my machine) looks at the class of every object it contains, and then just calls #underscore and #pluralize on it like so:

options[:root]     ||= all? { |e| e.is_a?(first.class) && first.class.to_s != "Hash" } ? first.class.to_s.underscore.pluralize : "records"

That doesn't work if the class name is nested.

The workaround is to specify the root:

puts [f].to_xml(:root => "bars")