Best practice to create a JSON string of one object containing anarray of other objects using Ruby?

M

Matthias S.

What is a good practice to create a JSON string of one object (object of
class A) containing an array of objects (objects of class B)? I am
particularly interessted in the implementation of class's A to_json
method.

Assuming class A looks as follows:

class A
attr_accessor :items
def initialize()
@items = Array.new
end
def to_json(*a)
?SECRET OF THE DAY?
end
end

and class B:

class B
def to_json(*a)
{"class B" => "class B"}.to_json(*a)
end
end

The best solution I got so far is:

def to_json(*a)
json = Array.new
@items.each do |item|
json << item.to_json(*a)
end
{"class A" => json}.to_json(*a)
end

Assuming there is only one item in array of an object of class A, the
resulting JSON string looks as follows:

{"class A":["{\"class B\":\"class B\"}"]}

I also tried:

def to_json(*a)
{"class A" => @items}.to_json(*a)
end

But it results in IOError exception in class A, saying to_json "not
opened for reading"...

I am sure we can do better?
 
B

Brian Candler

But it results in IOError exception in class A, saying to_json "not
opened for reading"...

I am sure we can do better?

I think it would be much better to make the class an attribute of the
JSON object, not build a new JSON object of {class => {attributes}}

If you look at the tests/ directory of the json or json_pure gem, you'll
see that they use 'json_class' for this purpose. e.g.

class A
def initialize(a)
@a = a
end

attr_reader :a

def ==(other)
a == other.a
end

def self.json_create(object)
new(*object['args'])
end

def to_json(*args)
{
'json_class' => self.class.name,
'args' => [ @a ],
}.to_json(*args)
end
end

The use of the 'args' element (being the array of arguments to #new) is
fairly ugly. I would rather expose the instance variables as members of
the JSON object:

class A
def initialize(a)
@a = a
end

attr_reader :a

def self.json_create(object)
new(object['a'])
end

def to_json(*args)
{
'json_class' => self.class.name,
'a' => @a,
}.to_json(*args)
end
end

a1 = A.new([123, 456])
puts a1.inspect
str = a1.to_json
puts str
a2 = JSON.parse(str)
puts a2.inspect

HTH,

Brian.
 
M

Matthias S.

@Brian: Both approaches you proposed result in IOError exception in
class A, saying to_json "not opened for reading".

Besides adding a class attribute, both approaches are actually similar
to the second I mentioned...
 
B

Brian Candler

Matthias S. wrote in post #980645:
@Brian: Both approaches you proposed result in IOError exception in
class A, saying to_json "not opened for reading".

Can you paste the *exact* exception you see, and the exact code which
triggers it? Plus details of the platform you are on.

It works for me:

$ ruby -v
ruby 1.8.7 (2010-06-23 patchlevel 299) [x86_64-linux]
$ ruby ert.rb
#<A:0x7f23333ad7e0 @a=[123, 456]>
{"a":[123,456],"json_class":"A"}
#<A:0x7f23333a7598 @a=[123, 456]>

That's running the code I posted, just with the addition of

require 'rubygems'
require 'json' # also works with require 'json/pure'

at the top. I have the json-1.4.3 gem installed, and json_pure-1.4.3.

Regards,

Brian.
 
B

Brian Candler

And here is a working example for your code where class A includes
instances of class B:

$ cat ert2.rb
require 'rubygems'
require 'json'

class A
attr_accessor :items
def initialize()
@items = Array.new
end
def to_json(*a)
{
"json_class" => self.class.name,
"items" => @items,
}.to_json(*a)
end
end

class B
def to_json(*a)
{
"json_class" => self.class.name,
}.to_json(*a)
end
end

a = A.new
a.items << B.new
a.items << B.new
puts a.to_json

$ ruby ert2.rb
{"items":[{"json_class":"B"},{"json_class":"B"}],"json_class":"A"}
 
M

Matthias S.

Running "ert2.rb" in console worked as expected.

I want to use to_json capability within a rails project. In my project,
I created class A and B as well as a controller to call the to_json
method. I got:

{"json_class":"A","items":[{},{}]}

After that, I reinstalled json and json_pure, using version 1.5.1 of
both now.

Now it works as expected... @Thanks to Brian

Rails 3.0.3
ruby 1.9.2p136 (2010-12-25 revision 30365) [i386-darwin9.8.0]
json 1.5.1
json_pure 1.5.1

So, reinstalling/upgrading json libs might solve the problem...
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,968
Messages
2,570,152
Members
46,697
Latest member
AugustNabo

Latest Threads

Top