4

In ruby how can i parse a json to an array of objects?
Example: i have 2 classes:

class Person 
  attr_accessor :name, :address, :email, :address
end

And:

class Address
  attr_accessor :street, :city, :state, :person
end

When i make a request i get the following json:

{
  "data": [
    {
      "id": 9111316,
      "name": "Mason Lee",
      "email": "[email protected]",
      "address": {
        "state": "American Samoa",
        "street": "Cameron Court",
        "city": "Wakulla"
      }
    },
    {
      "id": 500019,
      "name": "Stella Weeks",
      "email": "[email protected]",
      "address": {
        "state": "Nevada",
        "street": "Lake Street",
        "city": "Wacissa"
      }
    }
  ]
}

This json should be parsed into an array of Person.
For now i'm doing:

#json gem
require 'json'

#...
#parse the json and get the 'data'
parsed_json = JSON.parse json
json_data = parsed_json['data']

objects = Array.new
if json_data.kind_of?(Array)

  #add each person
  json_data.each { |data|
    current_person = Person.new
    data.each { |k, v|
      current_person.send("#{k}=", v)
    }
    objects.push(current_person)
  }
end

#return the array of Person
objects

I have a lot of objects like the above example and do this parse manually is not desirable. There is an automated way to do this?

By "automated way" i mean something like in java with jackson:

ObjectMapper mapper = new ObjectMapper();
List<Person> myObjects = mapper.readValue(json, mapper.getTypeFactory().constructCollectionType(List.class, Person.class));
4
  • Try searching the web for keywords like "ruby json serialization", e.g. github.com/harmoni/json-serializer, github.com/rails-api/active_model_serializers, etc Commented May 26, 2015 at 17:12
  • 1
    I wrote a gem to do this though you can't specify the class at the moment, they are generated dynamically -- github.com/allcentury/classy_json Commented May 26, 2015 at 17:37
  • @maerics In my case i need to deserialize the json, and i'm still searching about it Commented May 26, 2015 at 17:47
  • @Anthony it works fine. I've not found your gem in any repository, are you planning to publish your gem soon? The unique problem in my case is that i need specify the class. Anyway thank you Commented May 26, 2015 at 19:23

2 Answers 2

3

You can initialize the Person with the hash:

json_data = JSON.parse(json)['data']
json_data.map do |data|
  Person.new data
end

class Person 
  attr_accessor :name, :email, :address

  def initialize params
    params.each { |k,v| klass.public_send("#{k}=",v) }
  end 
end

If you want to choose the class dynamically, you can use:

json_data.map do |data|
  klass = 'Person'
  klass.get_const.new data
Sign up to request clarification or add additional context in comments.

3 Comments

The question does not really say anthing about specifying the class dynamically: List<Person> myObjects. Also, the class is not specified in the JSON.
very well i have removed my comment. I still prefer your pattern and have added it to my gist. What I actually meant is that he did not want to have to keep writing json_data.map and then having to specify the the class name in the loop. The OP is looking for a more automatic construct to handle this where data is passed with it's conversion class and then returned appropriately.
It would be simple to create a method out of the code that accepts a class param. However, it seems to me that doesn't really make sense because in order for it to be "automatic" the class would need to be specified in the JSON.
2

Why not just make the method yourself? Example:

require 'json'
def parse_json_to_class_array(data,root_node,to_klass)
  json_data = JSON.parse(data)[root_node]
  if json_data.is_a?(Array)
    objects = json_data.map do |item|
      klass = to_klass.new
      item.each { |k,v| klass.public_send("#{k}=",v) }
      klass
    end
  end 
  objects ||= []
end

Then for your example you could call it like so

json ="{\"data\":[
          {\"id\":9111316,
           \"name\":\"Mason Lee\",
           \"email\":\"[email protected]\",
           \"address\":{
              \"state\":\"American Samoa\",
              \"street\":\"Cameron Court\",
              \"city\":\"Wakulla\"
            }
          },
           {\"id\":500019,
            \"name\":\"Stella Weeks\",
            \"email\":\"[email protected]\",
            \"address\":{
               \"state\":\"Nevada\",
               \"street\":\"Lake Street\",
               \"city\":\"Wacissa\"
             }
           }
         ]
       }"
class Person 
  attr_accessor :id, :name,:email, :address
end


parse_json_to_class_array(json,'data',Person) 
#=>[#<Person:0x2ede818 @id=9111316, @name="Mason Lee", @email="[email protected]", @address={"state"=>"American Samoa", "street"=>"Cameron Court", "city"=>"Wakulla"}>, 
    #<Person:0x2ede7a0 @id=500019, @name="Stella Weeks", @email="[email protected]", @address={"state"=>"Nevada", "street"=>"Lake Street", "city"=>"Wacissa"}>]

Obviously you can expand this implementation to support single objects as well as overwrite Person#address= to perform the same operation and turn the address Hash into an Address object as well but this was not shown in your example so I did not take it this far in my answer.

A more dynamic example can be found Here

3 Comments

thank you for the snippet, since i've not found any gem to do this, looks like it will be better make the method as you said
@Victor in that case I have taken the time to create a more dynamic version that will also create the Address object and added it to the end of my post.
I'll have to change some things to fit other scenarios, but it's a good base to start :) thank you

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.