I have the following code:
class EpisodeIndex::API
def initialize
#url = "https://www.officeapi.dev/api/episodes?limit=400"
end
def get_episode_data
uri = URI.parse(#url)
response = Net::HTTP.get(uri)
data = JSON.parse(response)
data["data"].each do |episode|
get_episode_title(episode["title"])
end
end
def get_episode_title(title)
uri = URI.parse(title)
response = Net::HTTP.get(title)
data = JSON.parse(response)
binding.pry
end
EpisodeIndex::API.new.get_episode_data
end
and I'm getting this error in return.
`get_response': undefined method `hostname' for "Pilot":String (NoMethodError)
jocelynpeters#Jocelyns-Air office_cli %
I have no idea how to fix it. Be kind, please. I'm very new to programming.
Thanks!
The data return by the API looks like this:
{
"data":
[
{
"_id":"5e94d646f733a1332868e1dc",
"title":"Pilot",
"description":"A documentary crew gives a firsthand introduction to the staff of the Scranton branch of the Dunder Mifflin Paper Company, managed by Michael Scott.",
"writer": {
"_id":"5e95242f9511994a07f9a319",
"name":"Greg Daniels",
"role":"Writer/Director",
"__v":0
},
"director": {
"_id":"5e9523649511994a07f9a313",
"name":"Ken Kwapis",
"role":"Director",
"__v":0
},
"airDate":"2005-03-24T06:00:00.000Z",
"__v":0
},
# ...
That means the result set already includes the title of each episode and it doesn't include any URL at which you could load further information (what you currently try in your get_episode_title method). Therefore you can simplify your code to:
module EpisodeIndex
require "json"
require "net/http"
class API
def initialize
#url = "https://www.officeapi.dev/api/episodes?limit=400"
end
def titles
uri = URI.parse(#url)
response = Net::HTTP.get(uri)
data = JSON.parse(response)
data["data"].map do |episode|
episode["title"]
end
end
end
end
EpisodeIndex::API.new.titles
#=> ["Pilot", "Diversity Day", "Health Care", "The Alliance", "Basketball", "Hot Girl", "The Dundies", "Sexual Harassment", "Office Olympics", "The Fire", "Halloween", "The Fight", "The Client", "Performance Review", "E-Mail Surveillance", "Christmas Party", "Booze Cruise", "The Injury", "The Secret", "The Carpet", "Boys and Girls", "Valentine's Day", "Dwight's Speech", "Take Your Daughter to Work Day", "Michael's Birthday", "Drug Testing", "Conflict Resolution", "Casino Night"]
Related
I'm trying to write an update method that processes JSON. The JSON looks like this:
{
"organization": {
"id": 1,
"nodes": [
{
"id": 1,
"title": "Hello",
"description": "My description."
},
{
"id": 101,
"title": "fdhgh",
"description": "My description."
}
]
}
}
My update method is as follows:
def update
organization = Organization.find(params[:id])
nodes = params[:organization][:nodes]
nodes.each do |node|
n = Node.find(node[:id])
unless n.update_attributes(node_params)
render json: organization, status: :failed
end
end
render json: diagram, status: :ok
end
private
def node_params
params.require(:organization).permit(nodes: [:title, :description])
end
Unfortunately, n.update_attributes(node_params) generates:
Unpermitted parameter: id
Unpermitted parameter: id
Unpermitted parameter: id
(0.2ms) BEGIN
(0.3ms) ROLLBACK
*** ActiveRecord::UnknownAttributeError Exception: unknown attribute 'nodes' for Node.
Does anyone see what I'm doing wrong and to write this update method?
On the unless n.update_attributes(node_params) line, you're trying to update Node n with nodes_params, which are all of the nodes from your JSON minus the ids:
{"nodes"=>[{"title"=>"Hello", "description"=>"My description."}, {"title"=>"fdhgh", "description"=>"My description."}]}
You could just add :id as a permitted node parameter, cut out the nodes assignment step, iterate over node_params instead, and just omit the :id when updating Node n. E.g.,
def update
organization = Organization.find(params[:id])
node_params.each do |node|
n = Node.find(node[:id])
unless n.update_attributes(node.except(:id))
render json: organization, status: :failed
end
end
render json: diagram, status: :ok
end
private
def node_params
params.require(:organization).permit(nodes: [:id, :title, :description])
end
Okay, it's been many days. I have been going back and forth with this api, and would like to get the following results.
Here is the problem.
uri = URI('https://api.wmata.com/StationPrediction.svc/json/GetPrediction/All')
uri.query = URI.encode_www_form({'api_key' => 'ihaveit',})
request = Net::HTTP::Get.new(uri.request_uri)
#response = Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https') do |http|
http.request(request)
#data = JSON.parse #response.body
Now i've the #data parsed as JSON. I am trying to write a class Trains and use the following data.
{
"Car": "6",
"Destination": "SilvrSpg",
"DestinationCode": "B08",
"DestinationName": "Silver Spring",
"Group": "1",
"Line": "RD",
"LocationCode": "A01",
"LocationName": "Metro Center",
"Min": "3"
},
Here is the code for class Trains
class Trains
def initialize(car, destination, destinationcode, destinationname, group, line, locationcode, locationname)
#car = car
#destination = destination
#destinationcode = destinationcode
#destinationname = destinationname
#group = group
#line = locationcode
#locationname = locationname
end
And now im stuck about the next step. I am totally new to api. I can write the following for a static class.
def to_s
puts(#car + #destination + #destinationcode + #destinationname + #group + #line + #locationname)
end
= Trains.new
puts Train
end
I've got this so far.
class TrainLoader < Struct.new(:car, :destination, :destinationcode, :destinationname, :group, :line, :locationcode, :locationname)
class Trains
end
t = Trains.new(#data["Car"],#data["DestinationCode"], #data[DestinationName],#data[Group],#data[Line], #data[LocationCode], #data[LocationName], #data[Min])
You already have a class, just write a to_s funtcion:
class Trains
def initialize(car, destination, destinationcode, destinationname, group, line, locationcode, locationname)
#car = car
#destination = destination
#destinationcode = destinationcode
#destinationname = destinationname
#group = group
#line = locationcode
#locationname = locationname
end
# inspect attributes with own `to_s` method
def to_s
"#{#car} #{#destination} #{#destinationcode} #{#destinationname}"
end
end
Now create an instance of your class:
>> train = Trains.new(#data["Car"], #data["Destination"], #data["DestinationCode"])..
>> train.to_s
But i can suggest you a more elegant solution, with struct:
>> data = {
?> "Car": "6",
?> "Destination": "SilvrSpg",
?> "DestinationCode": "B08",
?> "DestinationName": "Silver Spring",
?> "Group": "1",
?> "Line": "RD",
?> "LocationCode": "A01",
?> "LocationName": "Metro Center",
?> "Min": "3"
>> }
>> class Trains < Struct.new(*data.keys)
#> # create a struct with attributes like keys in data hash `"Cat"` or `"Min"`
>> end
>> t = Trains.new(*data.values)
# create instance of Trains class with values from a data hash `"6"`
=> #<struct Trains Car="6", Destination="SilvrSpg", DestinationCode="B08", DestinationName="Silver Spring", Group="1", Line="RD", LocationCode="A01", LocationName="Metro Center", Min="3">
>> t.Car
=> "6"
>>
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 am trying to write a Jekyll extension that will embed comments from a Blogger blog.
I am able to fetch the comments feed as JSON, and process it enough to pull out the total number of comments. However, I have not figured out how to process each comment in the feed.
json_url = "http://www.blogger.com/feeds/8505008/593465383646513269/comments/default/?alt=json"
json_rep = Net::HTTP.get_response(json_url)
json_rep = JSON.parse(json_rep.body)
json_rep['feed']['openSearch$totalResults']['$t'] # => "4"
json_rep['feed']['entry'].class # => Array
json_rep['feed']['entry'].length
# => Liquid Exception: undefined method `length' for nil:NilClass in post
This is my first time writing any code in Ruby. What am I doing wrong?
Here are the relevant parts of the JSON I am trying to parse.
{
"feed": {
"openSearch$totalResults": {
"$t": "4"
},
"entry": [
{
"id": {
"$t": "tag:blogger.com,1999:blog-8505008.post-491866073982779922"
},
"published": {
"$t": "2013-01-08T15:23:47.322-04:00"
},
"content": {
"type": "html",
"$t": "Recently, my sister has updated it more than I have. \u00dcber-fail on my part. :p"
}
}
]
}
}
This is what you should look at doing:
require 'rubygems'
require 'json'
require 'net/http'
require 'net/https'
require 'uri'
url = "http://www.blogger.com/feeds/8505008/593465383646513269/comments/default/?alt=json"
uri = URI.parse(url)
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Get.new(uri.request_uri)
response = http.request(request)
json_rep = JSON.parse(response.body)
puts json_rep['feed']['openSearch$totalResults']['$t']
entries = json_rep['feed']['entry']
entries.each do |entry|
puts entry["id"]["$t"]
#add what ever code you like here
end
This outputs:
4
tag:blogger.com,1999:blog-8505008.post-491866073982779922
tag:blogger.com,1999:blog-8505008.post-4792479891671746788
tag:blogger.com,1999:blog-8505008.post-4766604955439002209
tag:blogger.com,1999:blog-8505008.post-5484003770204916000
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.