Ruby on Rails - ActiveRecord#build_from_xml function
I was playing with the new to_xml
feature of Ruby on Rails and I found myself wondering... if you can create XML from ActiveRecord objects, why can't you create ActiveRecord objects from XML?
After searching for a while in the RoR Documentation I wasn't able to find the inverse functionality of to_xml
. So now, it seems, I have an opportunity to contribute back to the Rails community with an a functional improvement of my own. I announce to you the build_from_xml
method to ActiveRecord.
Just place the below code in your config/environment.rb
file.
require "rexml/document"
module ActiveRecord
class Base
def self.build_from_xml(xml)
xml = REXML::Document.new(xml) if xml.class == String
ar = self.new
xml.elements[1].elements.each do | ele |
sym = ele.name.underscore.to_sym
# An association
if ele.has_elements?
klass = self.reflect_on_association(sym).klass
ar.__send__(sym) << klass.build_from_xml(ele)
# An attribute
else
ar[sym] = ele.text
end
end
return ar
end
end
end
You can call this from the main class of any ActiveRecord object. Here is an example.
This ruby code:
firm_xml = File.new("firm_data.xml").read
firm = Firm.build_from_xml(firm_xml)
Will convert this XML file into a fully functional ActiveRecord object, including the associations.
<firm>
<rating type="integer">1</rating>
<name>37signals</name>
<clients>
<client>
<rating type="integer">1</rating>
<name>Summit</name>
<id type="integer">1</id>
<firm-id type="integer">1</firm-id>
</client>
<client>
<rating type="integer">1</rating>
<name>Microsoft</name>
<id type="integer">2</id>
<firm-id type="integer">1</firm-id>
</client>
</clients>
<accounts>
<account>
<id type="integer">1</id>
<firm-id type="integer">1</firm-id>
<credit-limit type="integer">50</credit-limit>
</account>
</accounts>
<id type="integer">1</id>
</firm>
You may have noticed one caveat. This function accepts well formed XML code only that conforms to your model. If it doesn't, it may produce unpredictable results but will probably raise the usual ActiveRecord exceptions in most non-trivial error cases. Oh, and it requires REXML, but you knew that already right.
I will probably convert this to a plugin in the not-to-distant future. That is if the code isn't included in Rails' release branch (hint, hint).