How do I parsing data from JSON object? - ruby

I'm just starting to dabble in consuming a JSON web service, and I am having a little trouble working out the best way to get to the actual data elements.
I am receiving a response which has been converted into a Ruby hash using the JSON.parse method. The hash looks like this:
{"response"=>{"code"=>2002, "payload"=>{"topic"=>[{"name"=>"Topic Name", "url"=>"http://www.something.com/topic", "hero_image"=>{"image_id"=>"05rfbwV0Nggp8", "hero_image_id"=>"0d600BZ7MZgLJ", "hero_image_url"=>"http://img.something.com/imageserve/0d600BZ7MZgLJ/60x60.jpg"}, "type"=>"PERSON", "search_score"=>10.0, "topic_id"=>"0eG10W4e3Aapo"}]}, "message"=>"Success"}}
What I would like to know, is what is the easiest way to get to the "topic" data so I can do something like:
topic.name = json_resp.name
topic.img = jsob_resp.hero_image_url
etc

You can use Hashie's Mash . One of the best twitter clients for ruby uses it, and the resulting interface is very clean and easy to use. I've wrapped over Delicious rss api with it in less than 60 lines.
As usuall, the specs show very clearly how to use it.

Related

Extracting value from complex hash in Ruby

I am using an API (zillow) which returns a complex hash. A sample result is
{"xmlns:xsi"=>"http://www.w3.org/2001/XMLSchema-instance",
"xsi:schemaLocation"=>"http://www.zillow.com/static/xsd/SearchResults.xsd http://www.zillowstatic.com/vstatic/5985ee4/static/xsd/SearchResults.xsd",
"xmlns:SearchResults"=>"http://www.zillow.com/static/xsd/SearchResults.xsd", "request"=>[{"address"=>["305 Vinton St"], "citystatezip"=>["Melrose, MA 02176"]}],
"message"=>[{"text"=>["Request successfully processed"], "code"=>["0"]}],
"response"=>[{"results"=>[{"result"=>[{"zpid"=>["56291382"], "links"=>[{"homedetails"=>["http://www.zillow.com/homedetails/305-Vinton-St-Melrose-MA-02176/56291382_zpid/"],
"graphsanddata"=>["http://www.zillow.com/homedetails/305-Vinton-St-Melrose-MA-02176/56291382_zpid/#charts-and-data"], "mapthishome"=>["http://www.zillow.com/homes/56291382_zpid/"],
"comparables"=>["http://www.zillow.com/homes/comps/56291382_zpid/"]}], "address"=>[{"street"=>["305 Vinton St"], "zipcode"=>["02176"], "city"=>["Melrose"], "state"=>["MA"], "latitude"=>["42.466805"],
"longitude"=>["-71.072515"]}], "zestimate"=>[{"amount"=>[{"currency"=>"USD", "content"=>"562170"}], "last-updated"=>["06/01/2014"], "oneWeekChange"=>[{"deprecated"=>"true"}], "valueChange"=>[{"duration"=>"30", "currency"=>"USD", "content"=>"42749"}], "valuationRange"=>[{"low"=>[{"currency"=>"USD",
"content"=>"534062"}], "high"=>[{"currency"=>"USD", "content"=>"590278"}]}], "percentile"=>["0"]}], "localRealEstate"=>[{"region"=>[{"id"=>"23017", "type"=>"city",
"name"=>"Melrose", "links"=>[{"overview"=>["http://www.zillow.com/local-info/MA-Melrose/r_23017/"], "forSaleByOwner"=>["http://www.zillow.com/melrose-ma/fsbo/"],
"forSale"=>["http://www.zillow.com/melrose-ma/"]}]}]}]}]}]}]}
I can extract a specific value using the following:
result = result.to_hash
p result["response"][0]["results"][0]["result"][0]["zestimate"][0]["amount"][0]["content"]
It seems odd to have to specify the index of each element in this fashion. Is there a simpler way to obtain a named value?
It looks like this should be parsed into XML. According to the Zillow API Docs, it returns XML by default. Apparently, "to_hash" was able to turn this into a hash (albeit, a very ugly one), but you are really trying to swim upstream by using it this way. I would recommend using it as intended (xml) at the start, and then maybe parsing it into an easier to use format (like a JSON/Hash structure) later.
Nokogiri is GREAT at parsing XML! You can use the xpath syntax for grabbing elements from the dom, or even css selectors.
For example, to get an array of the "content" in every result:
response = #get xml response from zillow
results = Nokogiri::XML(response).remove_namespaces!
#using css
content_array = results.css("result content")
#same thing using xpath:
content_array = results.xpath("//result//content")
If you just want the content from the first result, you can do this as a shortcut:
content = results.at_css("result content").content
Since it is indeed XML dumped into a JSON, you could use JSONPath to query the JSON

How to import tweets with only the hashtag?

I'm looking to write a script to import tweets by only grabbing tweets with the designated hashtag. And I'm a little confused on how I would go about doing that. Ideally I would like just the raw data. Nothing fancy.
I tried working with tweetstream in Ruby but I see the majority of Twitter API is in JSON, which I'm not too familiar with. Anyways, does anyone have an idea of how I can simply import tweets based on a hashtag? Maybe stream it in my terminal?
JSON is the foundation of many web APIs. I think the general consensus would be...
Learn JSON
It really is not that hard of a notation. Here is a tutorial specifically on JSON in Ruby.
Example:
require 'json'
json_result = "{'tweet': '#awesomesauce is for the #winner'}" # From Twitter API
results = JSON.parse(json_result)
# => {:tweet => "#awesomesauce is for the #winner"}

Refactor my ruby snippet so it doesn't look like C anymore: method(method(param))

I have a class which uses a connection object to send the request data created by a request_builder object.
The code looks like this:
connection.send_request(request_builder.build_request(customer))
This in turn is called by
build_report(customer, connection.send_request(request_builder.build_request(customer)))
Ugly! Any ideas on how to make it more expressive? Usually in ruby and OOP we chain objects like this: "string".make_it_bigger.flash_it.send
It's code, that how it looks. But you can make yourself a favour by not trying to cram everything together on one line:
request = request_builder.build_request(customer)
response = connection.send_request(request)
report = build_report(customer, response)
if you told us more about your code base we might be able to suggest something else, but you don't give us very much to go on. What does the request_builder object do? Does connection.send_request(...) return a response? Why does a report need a customer and a response (assuming that's what is returned by connection.send_request(...)), and so on.
build_report(customer, request_builder.build_request(customer).send_over(connection))

How do you print JSON from the mongdb ruby driver?

When I do a find query from the mongodb JavaScript console, I get a beautiful JSON response. However, when I do a find with the ruby driver, it stores the JSON as some Ruby object. How do I convert this back to JSON? Or, better yet, how do I just get the JSON without doing the extra conversions?
I got it working. I was using ruby-1.9.1-p378 with rvm. I removed that. Now, I'm using the system ruby-1.8.7-p174 that came with the SnowLeopard install DVD. But, I was still getting an error with the to_json method except this time it was saying, stack level too deep. I did:
require 'json/pure'
instead of
require 'json'
Then, I changed the code to look something like this:
http://github.com/banker/mongulator/blob/master/mongulator.rb#L53
Here's the relevant part of the code:
cursor = persons.find(
{"loc" => {"$near" => [params[:lat].to_f, params[:lng].to_f]}},
{:limit => 20})
content_type "application/json"
JSON.pretty_generate(cursor.to_a)
The complete file is here:
http://github.com/acani/acani-sinatra/blob/master/acani.rb
And, it worked, even with pretty json like the facebook graph api gives you. To return the JSON all on one line, just do something like:
cursor.to_a.to_json
The JSON Code you get on the javascript console is also converted. It's not the native output of MongoDB, the Native format is BSON. To Get JSON on the javascript console it must be converted. In Ruby you should be able to do the same thing with the .to_json instance method of your Object.
now you can do cursor.find.to_a.inspect

SOAP::RPC::Driver formatting problems. How can I change it?

I'm dealing with a SOAP webservice call from a server that is expecting to receive method calls with the paramaters in the format of:
<urn:offeringId> 354 </urn:offeringId>
But SOAP::RPC::Driver is generating messages in the form of:
<offeringId xsi:type = "xsd:int">354</offeringId>
The server keeps erroring when it gets these messages (especially since it's expecting offeringId to be a custom type internal to itself, not an int).
Is there anyway to configure the driver to format things the way the server is expecting it. Is the server even doing SOAP? I'm having trouble finding reference to that style of formating for SOAP (I know it DOES work though, because SOAPUI works just fine with that type of message).
-Jenny
Edit: I've got at least part of it solved. the RPC::Driver (obviously) uses the RPC standard, whereas apparently the server I'm trying to talk to is doing "document". Now, when I look at RPC::Driver's API, I'm seeing a method named "add_document_method". That SOUNDS to me like it might be what I want, but I can't figure out what paramaters to give it. The examples I've seen around the net don't make much sense to me, things like:
def GetNamePair(response)
response.account.each do |x|
class << x
attr :configuration, true
end
x.configuration = Hash[*x.a.map do |y|
[y.__xmlattr[XSD::QName.new(nil, 'n')], String.new(y)]
end.flatten]
end
end
mNS = 'urn:zimbraAdmin'
drv.add_document_method('GetAllAdminAccountsRequest', mNS, [XSD::QName.new(mNS, 'GetAllAdminAccountsRequest')],
[XSD::QName.new(mNS, 'GetAllAdminAccountsResponse')] )
puts YAML.dump(GetNamePair(drv.GetAllAdminAccountsRequest([]))
All I really know is that I have a method that takes in certain parameters.... I really don't get why, if this method does what I think it does, it has to be more complicated. Isn't this just a matter of taking the exact same data and formating it differently? I'm so confused....
Okay, what I ended up doing was using SOAP:RPC:Drivers add_document_method, which requires me to give it the wsdl, namespace, etc, and then give it the attributes later as a single input hash thingy (and gives me the output in a similar format). It worked, it just wasn't as clean as add_rpc_method (which is waht add_method defaults to)
-Jenny

Resources