I am currently calling an API to get a publisher's Books. I am then grouping each book into different their respective story Arcs. Everything works great and I am able to retrieve the JSON in my app.
But, I'd like to change my current JSON response and remove the JSON root arcs:
Does anyone know how I can remove the JSON root and specify which book attributes I want?
Also, would a :has many => through association help simplify this and group things how i want?
This is my code.
Models
class Publisher < ActiveRecord::Base
has_many :books
end
class Arc < ActiveRecord::Base
has_many :books
end
class Book < ActiveRecord::Base
belongs_to :arc
belongs_to :book
end
Current Json
{
"arcs": [
{
"books": [
{
"arc_id": 1,
"created_at":"2014-12-27T20:54:46.518Z",
"id": 311,
"publisher_id": 7,
"updated_at":"2015-06-04T20:55:28.190Z"
}
],
"id": 1,
"name": "One-Shot"
}
]
}
How can I change it to this?
[
{
"books": [
{
"arc_id": 1,
"id": 311,
"publisher_id": 7
}
],
"id": 1,
"name": "One-Shot"
}
]
Controller
def index
#publisher = Publisher.find(params[:publisher_id])
#books = #publisher.books.order("positioning")
#results = {arcs: []}
#books.group_by(&:arc).each do |arc, books|
#results[:arcs] << {
id: arc.id,
name: arc.name,
books: books
}
end
respond_to do |format|
format.html
format.json { render :json => #results.to_json }
end
end
I have also tried using rabl in the link below, but it's not working..
https://stackoverflow.com/questions/30660136/include-group-by-parent-model-json-rabl
you could just render back without the arcs e.g.
format.json { render :json => #results[:arcs].to_json }
That being said you could also just change the controller method as well to this but you will have to change how the html response handles #results:
def index
#publisher = Publisher.find(params[:publisher_id])
#books = #publisher.books.order("positioning")
#results = #books.group_by(&:arc).map do |arc, books|
{
id: arc.id,
name: arc.name,
books: books
}
end
respond_to do |format|
format.html
format.json { render :json => #results.to_json }
end
end
This will also give you the desired result because map in this case will just return an Array of the Hashes you have designed.
Related
Here are my models:
class OrderItem < ApplicationRecord
belongs_to :order
belongs_to :product
end
class Order < ApplicationRecord
has_many :order_items
end
Here is my controller:
def index
orders = Order.where(nil)
render json: {'orders': orders}, include:
['order_items'], status: :ok
end
I want to also include the product in the order_items. How can I achieve this to get the following JSON:
{
"id": 2,
"order_items": [
{
"id": 1,
"product": {
"name": "abc"
},
}
]
},
You can reach this with changing
include: ['order_items']
to
include: ['order_items', 'order_items.product'].
More details you can get here.
I have been able to solve this by changing include: ['order_items'] to include: {'order_items': {include: 'product'}}
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": "normanodonnell#biospan.com",
"address": {
"state": "American Samoa",
"street": "Cameron Court",
"city": "Wakulla"
}
},
{
"id": 500019,
"name": "Stella Weeks",
"email": "hansenwhitfield#candecor.com",
"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));
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
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\":\"normanodonnell#biospan.com\",
\"address\":{
\"state\":\"American Samoa\",
\"street\":\"Cameron Court\",
\"city\":\"Wakulla\"
}
},
{\"id\":500019,
\"name\":\"Stella Weeks\",
\"email\":\"hansenwhitfield#candecor.com\",
\"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="normanodonnell#biospan.com", #address={"state"=>"American Samoa", "street"=>"Cameron Court", "city"=>"Wakulla"}>,
#<Person:0x2ede7a0 #id=500019, #name="Stella Weeks", #email="hansenwhitfield#candecor.com", #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
I have an employee controller which includes designation and department. Each one is separate model so i can access each api in the client side to fill each drop down. I need like view model concept in ASP.NET MVC.
For this i read work around like return 2 collections in one json object. But i am confused in the rabl file.
My controller code is below;
def employee_masters
#designations = Designation.all
#departments = Department.all
respond_with #designations
end
and my existing employee_masters.rabl is
collection :#designations
attributes :id, :name
please guide me how to change my rabl and controller code to achieve the behaviour. My expected json is below
{
"designations": [
{
"id": 1,
"name": "Program Manager"
},
{
"id": 2,
"name": "Project Manager"
},
{
"id": 3,
"name": "Tech Lead"
}
],
"departments": [
{
"id": 1,
"name": "IT"
},
{
"id": 2,
"name": "Support"
},
{
"id": 3,
"name": "Finance"
}
]
}
Modify:
I read this and got some idea. I created a class like below
class EmployeeViewModel
attr_accessor :departments, :designations
def initialize(departments, designations)
#departments = departments
#designations = designations
end
end
My controller code is
def employee_masters
#designations = Designation.all
#departments = Department.all
#employee_view_model = EmployeeViewModel.new(#departments, #designations)
respond_with #employee_view_model
end
And my rabl is
object #employee_view_model
child :departments do
extends 'api/v1/departments/show'
end
child :designations do
extends 'api/v1/designations/show'
end
Here shows error as Template::Error (undefined method `departments' for #<#:0x00000003872168>):
But when i change respond_with #employee_view_model to simply render :json => #employee_view_model it worked well and return the json. Please help to rectify rabl error
I looked into different resources and still get confused on how to parse a json format to a custom object, for example
class Resident
attr_accessor :phone, :addr
def initialize(phone, addr)
#phone = phone
#addr = addr
end
end
and JSON file
{
"Resident": [
{
"phone": "12345",
"addr": "xxxxx"
}, {
"phone": "12345",
"addr": "xxxxx"
}, {
"phone": "12345",
"addr": "xxxxx"
}
]
}
what's the correct way to parse the json file into a array of 3 Resident object?
Today i was looking for something that converts json to an object, and this works like a charm:
person = JSON.parse(json_string, object_class: OpenStruct)
This way you could do person.education.school or person[0].education.school if the response is an array
I'm leaving it here because might be useful for someone
The following code is more simple:
require 'json'
data = JSON.parse(json_data)
residents = data['Resident'].map { |rd| Resident.new(rd['phone'], rd['addr']) }
If you're using ActiveModel::Serializers::JSON you can just call from_json(json) and your object will be mapped with those values.
class Person
include ActiveModel::Serializers::JSON
attr_accessor :name, :age, :awesome
def attributes=(hash)
hash.each do |key, value|
send("#{key}=", value)
end
end
def attributes
instance_values
end
end
json = {name: 'bob', age: 22, awesome: true}.to_json
person = Person.new
person.from_json(json) # => #<Person:0x007fec5e7a0088 #age=22, #awesome=true, #name="bob">
person.name # => "bob"
person.age # => 22
person.awesome # => true
require 'json'
class Resident
attr_accessor :phone, :addr
def initialize(phone, addr)
#phone = phone
#addr = addr
end
end
s = '{"Resident":[{"phone":"12345","addr":"xxxxx"},{"phone":"12345","addr":"xxxxx"},{"phone":"12345","addr":"xxxxx"}]}'
j = JSON.parse(s)
objects = j['Resident'].inject([]) { |o,d| o << Resident.new( d['phone'], d['addr'] ) }
p objects[0].phone
"12345"
We recently released a Ruby library static_struct that solves the issue. Check it out.
I am on Rails 3.1, Mongoid 2.3.3, and using the nested_form gem. In my form, I have the nested_form link_to_add and link_to_remove set up to add and remove an embedded document in my model. The link_to_add helper method works great, but the link_to_remove help method changes are not persisted in MongoDB. In the rails output, I can see the JSON parameter passed to Mongoid has the _destroy: 1 value set but the change is not saved to MongoDB.
Here is the Model:
class MenuItem
include Mongoid::Document
include Mongoid::Timestamps
field :name
attr_accessible :name
embeds_many :ingredient_infos
accepts_nested_attributes_for :ingredient_infos, :allow_destory => true
attr_accessible :ingredient_infos_attributes
end
Here is the Controller's update method:
def update
#menu_item = MenuItem.find(params[:id])
respond_to do |format|
if #menu_item.update_attributes(params[:menu_item])
format.html { redirect_to #menu_item, notice: 'Menu item was successfully updated.' }
format.json { head :ok }
else
format.html { render action: "edit" }
format.json { render json: #menu_item.errors, status: :unprocessable_entity }
end
end
end
Here is the parameters sent to the controller:
{
"utf8"=>"✓",
"authenticity_token"=>"5abAWfFCr7hkzYXBEss75qlq8DMQ0pW5ltGmrgHwPjQ=",
"menu_item"=>
{
"name"=>"Bowl",
"ingredient_infos_attributes"=>
{
"0"=>
{
"ingredient"=>"Rice",
"_destroy"=>"false",
"id"=>"4eb1b0b118d72f1a26000022"
},
"1"=>
{
"ingredient"=>"Chicken",
"_destroy"=>"1",
"id"=>"4eb1b0b118d72f1a26000025"
}
}
},
"commit"=>"Update Menu item",
"id"=>"4eb1b0b118d72f1a2600001f"
}
In MongoDB, the Chicken document still exists; that document also shows up in the view online (the page pulls all the items in the embedded document).
I'm sure I missed something, but I haven't been able to figure out why the embedded document isn't removed.
yes, your :allow_destory should be :allow_destroy
I am experiencing the same issue -
RESOLVED with
accepts_nested_attributes_for :phones, :allow_destroy => true