to_json on a has_one embedded association results in an ActiveSupport::JSON::Encoding::CircularReferenceError

297 views
Skip to first unread message

Scott Tamosunas

unread,
Aug 18, 2010, 10:51:41 AM8/18/10
to MongoMapper
I have the following associations:

A View has many Components
A Component has one Data Binding
A Data Binding belongs to a Component

The View is a document while the Component and Data Binding are
embedded documents.

I create one View that has one Component and a component that has one
Data Binding.

When I try to to_json the view (view.to_json), I get the following
exception:

ActiveSupport::JSON::Encoding::CircularReferenceError: object
references itself

I've replicated this with both Rails 3 beta 4 and rc1.

This seems related to the has_one. If I change it to a has_many
relationship, to_json works fine.

Below are my models, if you create a new application, drop in these
models with mongo_mapper 0.8.3 in the Gemfile, you can easily
reproduce this from the console:

view = View.create!(:name => "view")
view.to_json
view.components << Component.new(:name => "component")
view.save!
view.to_json
view.components.first.data_binding = DataBinding.new(:name =>
"binding")
view.save!
view.to_json

Any ideas?

Thanks,

Scott


class View
include MongoMapper::Document

key :name, String
timestamps!

has_many :components
end

class Component
include MongoMapper::EmbeddedDocument

has_one :data_binding
end

class DataBinding
include MongoMapper::EmbeddedDocument

key :name, String

belongs_to :component
end

John Nunemaker

unread,
Aug 19, 2010, 8:38:57 AM8/19/10
to mongo...@googlegroups.com
I'll have to look into it. One thing you can do in the mean time is foo.attributes.to_json. Thanks for reporting.


--
You received this message because you are subscribed to the Google
Groups "MongoMapper" group.
For more options, visit this group at
http://groups.google.com/group/mongomapper?hl=en?hl=en

Scott Tamosunas

unread,
Aug 19, 2010, 11:04:09 AM8/19/10
to MongoMapper
Cool, I'll give that a shot.

I have been investigating and I get the feeling it has to do with
_root_document being in the hash that get's JSON'ed. The
_root_document is the View for the DataBinding object and we are still
descended into the View's JSON encoding, thus the view is in the @seen
array.

I tried adding DataBinding.include_root_in_json = false but that had
no effect.

Thanks,

Scott


On Aug 19, 8:38 am, John Nunemaker <nunema...@gmail.com> wrote:
> I'll have to look into it. One thing you can do in the mean time is
> foo.attributes.to_json. Thanks for reporting.
>

hamin

unread,
Aug 19, 2010, 2:43:05 PM8/19/10
to MongoMapper
Yeah I was running into this problem yesterday too and ended up using
@foo.attributes.to_json

Scott Tamosunas

unread,
Aug 24, 2010, 4:16:12 PM8/24/10
to MongoMapper
So it definitely has to do with trying to JSON root_document in the
data_binding. When you call view.to_json, it encodes the view first
and then cascades down through the associations. When it gets to the
data_binding, it tries to to_json the root_document, sees that we are
still trying to json the view, and raises the CircularReferenceError.

Have a look at activesupport-3.0.0.beta4/lib/active_support/json/
encoding.rb, line 57. If you trace through, you'll see the view object
put in the @seen array first and then later on through the
data_binding on the root_document.

The way I got around this was to remove the root and parent documents
from the hash before encoding. In my case, I don't need them, as I am
just using the JSON for the UI.

class View
include MongoMapper::Document
key :name, String
timestamps!
has_many :components

def as_json(options = {})
remove_root_and_parent_elements(super(options))
end

private

def remove_root_and_parent_elements(json)
if json.is_a?(Array)
json.inject([]) { |arr, element| arr <<
remove_root_and_parent_elements(element) }
elsif json.is_a?(Hash)
cleaned_hash = {}
json.each do |key, value|
cleaned_hash[key] = remove_root_and_parent_elements(value)
unless (key == "_parent_document" || key == "_root_document")
end
cleaned_hash
else
json
end
end
end

It would seem that the right solution would be to modify mongo_mapper/
plugins/serialization.rb#as_json to not serialize root_documents if
include_root_in_json is false, unless I misunderstand what the intent
of that is.

Thoughts?

Thanks,

Scott



On Aug 19, 2:43 pm, hamin <aminhar...@gmail.com> wrote:
> Yeah I was running into this problem yesterday too and ended up using
> @foo.attributes.to_json
>
> On Aug 19, 11:04 am, Scott Tamosunas <tamosu...@gmail.com> wrote:
>
>
>
> > Cool, I'll give that a shot.
>
> > I have been investigating and I get the feeling it has to do with
> > _root_document being in the hash that get'sJSON'ed. The
> > _root_document is the View for the DataBinding object and we are still
> > descended into the View'sJSONencoding, thus the view is in the @seen

hamin

unread,
Aug 24, 2010, 5:04:46 PM8/24/10
to MongoMapper
Hey Scott,

Nice! I like your soltuion a lot. Foo.attributes.to_json worked for me
but the json response from your method is cleaner. Thank you!

Haris

John Nunemaker

unread,
Aug 25, 2010, 9:50:51 AM8/25/10
to mongo...@googlegroups.com
Yep, makes sense.
Reply all
Reply to author
Forward
0 new messages