X-Combinator

Avatar

making the human scalable

ActiveRecord from_xml (and from_json) part 2

This post is an upgrade to the previous post about the unmarshalling of XML and JSON strings into rails objects, with arbitrarily deep object associations. As you may know if you are reading this, there doesn’t seem to be a way in rails to reverse to_xml and to_json when associations are included.

Example usage:

xml = firm.to_xml :include => [ :account, :clients ]  
firm = Firm.from_xml xml

firm.account and firm.clients will behave as intended (by default there does not seem to be a direct way in rails to unmarshall them.)

Here’s how to get from_xml and from_json defined for your ActiveRecord classes:

Create a file lib/extensions.rb with the following code:

module ActiveRecord
  class Base
 
    def self.from_hash( hash )
      h = hash.dup
      h.each do |key,value|
        case value.class.to_s
        when 'Array'
          h[key].map! { |e| reflect_on_association(
             key.to_sym ).klass.from_hash e }
        when /\AHash(WithIndifferentAccess)?\Z/
          h[key] = reflect_on_association(
             key.to_sym ).klass.from_hash value
        end
      end
      new h
    end
 
    def self.from_json( json )
      from_hash safe_json_decode( json )
    end
 
    # The xml has a surrounding class tag (e.g. ship-to),
    # but the hash has no counterpart (e.g. 'ship_to' => {} )
    def self.from_xml( xml )
      from_hash begin
        Hash.from_xml(xml)[to_s.demodulize.underscore]
      rescue ; {} end
    end
 
  end # class Base
end # module ActiveRecord
 
### Global functions ###
 
# JSON.decode, or return {} if anything goes wrong.
def safe_json_decode( json )
  return {} if !json
  begin
    ActiveSupport::JSON.decode json
  rescue ; {} end
end

At the bottom of config/environments.rb, insert the line:

require 'lib/extensions' # custom class extensions

You can name the “extensions.rb” file anything else you want; just update the environments.rb file accordingly.

This is an improvement over the previous post, in that it can work even when your associations use the :class_name parameter, you’re not editing the vendor/rails files directly, and it can deal with hashes of the rails class HashWithIndifferentAccess.

del.icio.us:ActiveRecord from_xml (and from_json) part 2 digg:ActiveRecord from_xml (and from_json) part 2 reddit:ActiveRecord from_xml (and from_json) part 2

One Comment, Comment or Ping

  1. Matt - Thanks for the code. Works great. You might want to mention that you have changed the paradigm of the from_xml. It currently is not a self method, so you need to create a object first. Your new method does not require this. I thought your code was not working at first until I noticed this.

Reply to “ActiveRecord from_xml (and from_json) part 2”