Array to_xml

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???

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.

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

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" ] } }

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: eli.st

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

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" )