Ruby on Rails - ActiveRecord#build_from_xml function
Monday, May 1, 2006 at 10:20PM
Wayne Robinson in article, programming, ruby on rails

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

Article originally appeared on Wayne Robinson's Blog (http://wayne-robinson.com/).
See website for complete article licensing information.