Forum: Ruby on Rails Array to_xml

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Sam (Guest)
on 2008-10-07 05:31
(Received via mailing list)
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 S. (Guest)
on 2008-10-07 05:58
(Received via mailing list)
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 C. (Guest)
on 2008-10-07 06:16
(Received via mailing list)
On 7 Oct 2008, at 02:30, Sam wrote:

>   from (irb):94
> \">\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 (Guest)
on 2008-10-07 06:55
(Received via mailing list)
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 S. (Guest)
on 2008-10-07 09:31
(Received via mailing list)
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...

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 (Guest)
on 2008-10-07 09:55
(Received via mailing list)
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" )
This topic is locked and can not be replied to.