Nested serialization with to_json and to_xml with array and hash element


#1

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.


#2

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


#3

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


#4

Hi again,

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

scho