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.
One Comment, Comment or Ping
Matt Brown
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.
Oct 30th, 2008
Reply to “ActiveRecord from_xml (and from_json) part 2”