Nested serialization with to_json and to_xml with array and hash element

Title: Nested serialization with to_json and to_xml with array and hash element

class Person < ActiveRecord::Base   has_one :address   has_one :job

  def office()     return "Executive_Suite"   end end

class Address < ActiveRecord::Base   belongs_to :person end

class Job < ActiveRecord::Base   belongs_to :person   has_one :paygrade

  def Title     return "Engineer"   end end

class PayGrade < ActiveRecord::Base   belongs_to: job

end

Given an instance of a Person, I am able to serialize in XML and JSON in some simpler nested manners such as this:

@person = Person.find_by_id(1)

# render the results with multiple peer associations with no drill down respond_to do |format|       format.json { render.json(:json=>@person.to_json(:include=> [:address, :job], :methods=>:office) }       format.xml { render.json(:json=>@person.to_json(:include=> [:address, :job], :methods=>:office) }       } end

# render the results with 1 association requiring deep drill down respond_to do |format|       format.json { render.json(:json=>@person.to_json(:include=> {:job=>{:include=>:paygrade}}) }       format.xml { render.json(:json=>@person.to_json(:include=>{:job=> {:include=>:paygrade}}) }       } end

Where I continually run into problems is where I have multiple peer associations at the same level with one of them needing further drill down into one of its association.

For instance I thought I could simply do this to serialize the Person instance, along with their address, job with Paygrade. Whenever I try to do this: respond_to do |format|       format.json { render.json(:json=>@person.to_json(:include=> [:address, {:job=>{:include=>:paygrade}}] }       format.xml { render.json(:json=>@person.to_json(:include=> [:address, {:job=>{:include=>:paygrade}}] }       } end

I always get the following error: You have a nil object when you didn't expect it. Error evaluating nil.ma. Based on my understanding the :include is expecting an array but I'm adding a hash as an element and Rails is not expecting it in the way I'm specifying?

Where I also continually run into problems is where I have multiple peer associations at the same level with one of them needing requiring a custom method to be serialized.

Question 1: What is the correct way to be able to specify that I need to serialize the Person with address and job along with the paygrade for the job? This is the multiple peers with deep drill down problem mentioned above.

Question 2: What is the correct way to be able to specify that I need to serialize the Person with address and job along with the custom Job Title method with the paygrade? When I try this, I get error:

respond_to do |format|       format.json { render.json(:json=>@person.to_json(:include=> [:address, {:job=>{:methods=>:title, :include=>:paygrade}}] }       format.xml { render.json(:json=>@person.to_json(:include=> [:address, {:job=>{:methods=>:title, :include=>:paygrade}}] }       } end

Please advise, and thanks in advance.

The error description above was slightly truncated. The full error is this: You have a nil object when you didn't expect it! The error occurred while evaluating nil.macro

Hi Folks,

I ran into the same problem. Here is my fix, which solved the problem (I've added it to my environment.rb):

module ActiveRecord   module Serialization     class Serializer       def add_includes_with_nested_include(&block)         if include_associations = options.delete(:include)           base_only_or_except = { :except => options[:except],                                   :only => options[:only] }

          include_has_options = include_associations.is_a?(Hash)           associations = include_has_options ? include_associations.keys : Array(include_associations)

          for association in associations             association_has_options = association.is_a?(Hash)             if association_has_options               macro_association = association.keys.first               additional_association_options = association [macro_association]             else               additional_association_options = {}               macro_association = association             end             macro = @record.class.reflect_on_association (macro_association).macro             records = case macro             when :has_many, :has_and_belongs_to_many               @record.send(macro_association).to_a             when :has_one, :belongs_to               @record.send(macro_association)             end             unless records.nil?               association_options = include_has_options ? include_associations[macro_association] : base_only_or_except               opts = options.merge(association_options).merge (additional_association_options)               yield(macro_association, records, opts)             end           end

          options[:include] = include_associations         end       end       alias_method_chain :add_includes, :nested_include     end   end end

If someone wants to commit this to github, more power to you!

scho

Hi again,

I found out, that the following works as well: Instead of [:address, {:job=>{:include=>:paygrade}}] try {:address => {}, {:job=>{:include=>:paygrade}}}

scho