Array to_xml

112 views
Skip to first unread message

Sam

unread,
Oct 6, 2008, 9:30:19 PM10/6/08
to Ruby on Rails: Talk
Why is it that...

{code}
>> myarray = ["one"]
=> ["one"]
>> myarray.to_xml
RuntimeError: Not all elements respond to to_xml
from /foo/vendor/rails/activerecord/lib/../../activesupport/lib/
active_support/core_ext/array/conversions.rb:62:in `to_xml'
from (irb):94
>>
{code}

... however ...

{code}
>> myarray2 = [{:one => "onevalue"}]
=> [{:one=>"onevalue"}]
>> myarray2.to_xml
=> "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<records type=\"array
\">\n <record>\n <one>onevalue</one>\n </record>\n</records>\n"
>>
{code}

Is there a way I can convert an array to xml without having to wrap
each element in a hash???

Michael Sofaer

unread,
Oct 6, 2008, 9:57:40 PM10/6/08
to Ruby on Rails: Talk
What do you want the resulting XML to look like? Usually XML has name/
value format.

You can do this:

class Symbol
def to_xml
{self => self.to_s}.to_xml
end
end

and then you can call :hello.to_xml

=> <hello>hello</hello>

That's a little dangerous, though, and it may turn out that you should
rethink the XML schema, and use hashes in general.

Frederick Cheung

unread,
Oct 6, 2008, 10:15:17 PM10/6/08
to rubyonra...@googlegroups.com

On 7 Oct 2008, at 02:30, Sam wrote:

>
> Why is it that...
>
> {code}
>>> myarray = ["one"]
> => ["one"]
>>> myarray.to_xml
> RuntimeError: Not all elements respond to to_xml
> from /foo/vendor/rails/activerecord/lib/../../activesupport/lib/
> active_support/core_ext/array/conversions.rb:62:in `to_xml'
> from (irb):94
>>>
> {code}
>
> ... however ...
>
> {code}
>>> myarray2 = [{:one => "onevalue"}]
> => [{:one=>"onevalue"}]
>>> myarray2.to_xml
> => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<records type=\"array
> \">\n <record>\n <one>onevalue</one>\n </record>\n</records>\n"
>>>


In a nutshell, strings don't have a to_xml method, but hashes do.

> {code}
>
> Is there a way I can convert an array to xml without having to wrap
> each element in a hash???
>

What do you want the output to look like ?

Fred
> >

Sam

unread,
Oct 6, 2008, 10:54:21 PM10/6/08
to Ruby on Rails: Talk
I'm wanting something like...

<errors>
<error>foo</error>
<error>bar</error>
</errors>

I find it really interesting that a Rails controller will accept xml
like...

<process-codes>
<code>one</code>
<code>two</code>
</process-codes>

.. and transform it into a hash..

{ :process_codes => { :code => [ "one", "two" ] } }

... but will not go back the other way when I want to convey errors...

{ :errors => { :error => [ "foo", "bar" ] } }

Michael Sofaer

unread,
Oct 7, 2008, 1:30:48 AM10/7/08
to Ruby on Rails: Talk
The standard thing to do is put classes in there instead of strings,
so each error would be an instance of class Error, which would respond
to to_xml with <error>foo</error> or whatever. See, for example, Pat
Maddox's Blog: http://evang.eli.st/blog/2007/2/22/my-rails-gotcha-custom-to_xml-in-a-hash-or-array

If you are worried about reversibility, you can change active_support
\core_ext\array\conversions so that the to_xml method looks like this
at the top:

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

raise "Not all elements respond to to_xml" unless all? { |e|
e.respond_to? :to_xml }

options[:children] ||= options[:root].singularize
options[:indent] ||= 2
options[:builder] ||= Builder::XmlMarkup.new(:indent =>
options[:indent])

The XML you get back isn't pretty, but it doesn't crash:

<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<hash>
<errors>
<error type=\"array\">
<error>
<error>foo</error>
</error>
<error>
<error>bar</error>
</error>
</error>
</errors>
</hash>

Cleaning it up more than this might be a bit tricky. If this is
something a lot of people would like, I'd look at it some more, but I
think it's pretty unusual, and it's usually easier to use objects.

-Michael

Sam

unread,
Oct 7, 2008, 1:54:33 AM10/7/08
to Ruby on Rails: Talk
Thanks Michael.

I've taken the easy (and hacky) way out by using an
ActiveRecord::Errors object which is transformed implements to_xml
quite nicely.
The hacky bit is that I'm not always adding an error that coincides
with an attribute of the model I'm exploiting .. eg,

errors.add( :code, "#{code} is not found" )

..when my model object doesn't actually have a "code" attribute.
Also... under some scenarios I may not have a model instance at all
but need to create one in order to gain access to the to_xml friendly
Errors object by doing something like...

errors = Player.new.errors

... which is of course hacky, but more readily thrown together than
implementing a custom errors class or modifying the conversions.rb
The Errors object is also nice because it allows multiple errors with
the same key...

errors.add( :code, "#{code} is not found" )
errors.add( :code, "#{code} has already been processed" )


Reply all
Reply to author
Forward
0 new messages