JSON object for just an integer - ruby

Silly question, but I'm unable to figure out..
I tried the following in Ruby:
irb(main):020:0> JSON.load('[1,2,3]').class
=> Array
This seems to work. While neither
JSON.load('1').class
nor this
JSON.load('{1}').class
works. Any ideas?

I'd ask the guys who programmed the library. AFAIK, 1 isn't a valid JSON object, and neither is {1} but 1 is what the library itself generates for the fixnum 1.
You'd need to do: {"number" : 1} to be valid json. The bug is that
a != JSON.parse(JSON.generate(a))

I'd say it's a bug:
>> JSON.parse(1.to_json)
JSON::ParserError: A JSON text must at least contain two octets!
from /opt/local/lib/ruby/gems/1.8/gems/json-1.1.3/lib/json/common.rb:122:in `initialize'
from /opt/local/lib/ruby/gems/1.8/gems/json-1.1.3/lib/json/common.rb:122:in `new'
from /opt/local/lib/ruby/gems/1.8/gems/json-1.1.3/lib/json/common.rb:122:in `parse'
from (irb):7
I assume you're using this: (http://json.rubyforge.org/)

JSON only supporting objects is simply not true -- json.org also does not suggest this imo. it was derived from javascript and thus especially strings and numbers are also valid JSON:
var json_string = "1";
var p = eval('(' + json_string + ')');
console.log(p);
// => 1
typeof p
// => "number"
ActiveSupport::JSON properly understands raw value JSON:
require 'active_support/json'
p = ActiveSupport::JSON.decode '1'
# => 1
p.class
# => Fixnum
and so does MultiJson:
require 'multi_json'
p = MultiJson.load '1'
# => 1
p.class
# => Fixnum
so, as a2800276 mentioned, this must be a bug.
but as of this writing, ruby 2's JSON has quirks_mode enabled by default when using the load method.
require 'json'
p = JSON.load '1'
# => 1
p.class
# => Fixnum

The first example is valid. The second two are not valid JSON data. go to json.org for details.

As said only arrays and objects are allowed at the top level of JSON.
Maybe wrapping your values in an array will solve your problem.
def set( value ); #data = [value].to_json; end
def get; JSON.parse( #data )[0]; end

From the very basics of what JSON is:
Data types in JSON can be:
Number
String
Json Object ... (and some more)
Reference to see complete list of Json data types
Now any Json data has to be encapsulated in 'Json Object' at the top level.
To understand why is this so, you can see that without a Json Object at the top level, everything would be loose and you could only have only one of the data type in the whole of Json. i.e. Either a number, a string, a array, a null value etc... but only one.
'Json Object' type has a fixed format of 'key' : 'value' pair.
You cannot store just the value. Thus you cannot have something like {1}.
You need to put in the correct format, i.e. 'key' : 'value' pair.

Related

Ruby parsing XML: no implicit conversion of String into Integer

I'm working on a Sinatra application that pulls in a list of dates through an XML file and then creates an hash of all the dates.
I'm running into a strange issue that's happening when I'm pulling the id and assigning it to a variable.
The error I'm getting is:
no implicit conversion of String into Integer and it's being thrown on the event_date_id = event_date["date_id"] line. I have almost identical method in my code and it's working just fine. When I puts event_date['date_id'] it gives me the correct numerical date_id.
Just in case it helps, the class of the event_date['date_id'] is REXMLUtiliyNodeString, same as the id field in the other method. If I try to event_date['date_id'].to_i it gives breaks at that point.
def get_dates(event_id)
url = "some_url_to_some_xml"
puts '==================='
puts "Pulling in #{url}"
puts '==================='
date_xml = Crack::XML.parse(open(url))
dates = {}
date_xml['document']['date'].each do | event_date |
event_date_id = event_date['date_id']
single_date = {
'date_id' => event_date_id,
'date_start' => event_date['datestart'],
'date_end' => event_date['dateend'],
'date_live' => event_date['live'],
'time_start' => event_date['timestart'],
'time_end' => event_date['timestart'],
'date_available' => event_date['date_available']
}
dates.merge!( event_date_id => single_date )
end
return dates
end
Here is the xml format:
<document>
<date>
<date_id>881908</date_id>
<live>y</live>
<datestart>2017-08-14</datestart>
<dateend>2017-08-15</dateend>
<timestart>13:00</timestart>
<timeend>0:00</timeend>
<date_available>10000</date_available>
</date>
<document>
I have a feeling this is something really simple but I'm wracking my brains trying to figure it out. If anyone could shed some light on this, I'd definitely appreciate it.
Edit 1: When I run the code in irb, it does indeed work without error. Somewhere, somehow it seems Sinatra, Rack or Shotgun are getting in the way.
I have found the cause of my issue. It only occurs when I'm parsing an xml file with one entry for date/event whatever.
This question explains the exact issue I was having and the answer includes a work around that worked for me.
if(!date_xml['document']['date'].is_a?(Array))
date_xml['document']['date'] = [ date_xml['document']['date'] ]
end
This message occurs when you try to use a string index to look up a value in an array.
2.0.0p353 :001 > results = [""]
=> [""]
2.0.0p353 :002 > results["x"]
TypeError: no implicit conversion of String into Integer
from (irb):2:in `[]'
from (irb):2
from /home/jeff/.rvm/rubies/ruby-2.0.0-p353/bin/irb:12:in `<main>'
Since arrays can only be accessed by integer indexes, Ruby attempts to transform your key name into an integer, and fails because it doesn't know what number should be used to represent arbitrary string data. Hence the message "no implicit conversion of String into Integer".
If you're experiencing this, the answer is to fix your code so that it doesn't try to access an array like a hash. If you're importing from XML or JSON data on the assumption that the key desired is always there and will always be imported, but you're still getting this, your assumption is wrong; the data is not formatted as expected. Either fix the data or fix the code to handle the differing format.
I randomly stumbled upon what may be a better answer to this question, although I am very inexperienced so I think it needs to be verified.
I had an identical issue and saw that much of the trouble seems to be because I was returning a somewhat confusing array of one object.
Once I added ".first" to my query, I was able to retrieve my intended attribute.
response = File.open('ncaa_bb_schedule.xml')
doc = Nokogiri::XML(response)
doc.remove_namespaces!
doc.xpath('//game').each do |game|
h = game.xpath('home').first
p h['id']
end
But my original query, shown here
response = File.open('ncaa_bb_schedule.xml')
doc = Nokogiri::XML(response)
doc.remove_namespaces!
doc.xpath('//game').each do |game|
h = game.xpath('home')
p h['id']
end
end
was giving me the same error: "TypeError: no implicit conversion of String into Integer." Hope that helps somebody as it is much shorter than the aforementioned workaround.

how non existing hash key returns empty string?

Here is the exact problem;
$ hash
=> {:createAuthenticationTokenRequest=>{:playerSessionID=>"111"}}
$ hash[:attributes!]
=> "" (here is the crazy result)
$ hash.class
=> Hash
$ hash.keys
=> [:createAuthenticationTokenRequest]
what is going on here? Am i not supposed to get nil for non existent hash keys ?
Detailed problem:
I am using savon to send a webservice request and getting "can't convert Symbol into Integer" error all the time, debugging the error with pry showed me that this line is getting executed as empty string which it shouldn't.
attributes = hash[:attributes!] || {}
Help me out here!
thanks in advance, cheers!
Update:
Answer for how the hash is created;
class Gyoku::Hash
def self.iterate_with_xml(hash)
xml = Builder::XmlMarkup.new
attributes = hash[:attributes!] || {}
Update2:
This is the request i am sending
request(
createAuthenticationTokenRequest: {
playerSessionID: "111"
}
)
As i mentioned before this is savon gem code that gets executed. I tried to write the question as less boring as possible, and don't get why it gets downvoted :/
here is the source code that gets debugged.
https://github.com/savonrb/gyoku/blob/master/lib/gyoku/hash.rb
i guess i deserved to be downvoted......
Don't do this in your code,
def default_request_parameters
#default_request_parameters || Hash.new('')
end
You can create a hash like this
way 1:
new_hash = Hash.new{|h,k| h[k] = ""}
new_hash['unknown_key']
it returns
=> ""
new_hash.keys
=> ['unknown_key']
this adds 'unknown_key' key to the hash.
way 2:
hash2 = Hash.new("")
hash2['unknown_key']
it returns
=> ""
but no keys are added.
hash2.keys
it returns
=> []

Extract a value from an OpenStruct Ruby object

I get the following Ruby object returned (from a query to the Google Analytics API using the garb gem, comes from the sample call shown on the README.md there, Exits.results(profile, :filters => {:page_path.eql => '/'}))
> data.results
=> [#<OpenStruct page_path="/", exits="3706", pageviews="10440">]
I'd to extract the pageviews value (10440), but cannot figure out how to do it. I see that my object, data.results is class array of length 1, but data.first is class OpenStruct with a return value that looks almost identical:
irb(main):140:0> data.results.class
=> Array
irb(main):141:0> data.results.length
=> 1
irb(main):142:0> data.first
=> #<OpenStruct page_path="/", exits="3706", pageviews="10440">
irb(main):143:0> data.first.class
=> OpenStruct
while data itself seems to be a custom return type called ResultsSet:
irb(main):144:0> data.class
=> Garb::ResultSet
irb(main):145:0> data
=> #<Garb::ResultSet:0x00000002411070 #results=[#<OpenStruct page_path="/", exits="3706", pageviews="10440">], #total_results=1, #sampled=false>
irb(main):146:0>
Lots of data structures, but no idea how to get my desired value out. I gathered OpenStruct was related to a hash, so I thought data.first["pageviews"] would do it,
NoMethodError: undefined method `[]' for #<OpenStruct page_path="/", exits="3706", pageviews="10440">
from (irb):146
from /usr/bin/irb:12:in `<main>'
Meanwhile data.first.keys returns nil. No idea how to get my data out, (short of converting the length-1 array, data.results to a string and parsing with grep, which seems crazy. Any ideas?
Please try this:
data.first.pageviews

TypeError: can't convert Builder::XmlMarkup to Array

I'm having issues with getting access to the raw xml from a Builder::XmlMarkup object.
irb> xml = Builder::XmlMarkup.new(:target => '')
=> <pretty_inspect/>
irb> xml.foo("bar")
=> "<pretty_inspect/><foo>bar</foo>"
irb> puts xml
TypeError: can't convert Builder::XmlMarkup to Array (Builder::XmlMarkup#to_ary gives String)
from (pry):122:in `puts'
In a script where I'm using Builder to create the XML, I'm passing #xml to a POST:
response = HTTParty.post(API_ENDPOINT, :body => #xml)
This gives the same error:
TypeError (can't convert Builder::XmlMarkup to Array (Builder::XmlMarkup#to_ary gives String)):
Of course, if I do #xml.to_xml, it doesn't return an error, but it adds </to_xml> to the xml, meaning it isn't actually converting the XML object to xml. That's not what I want.
So how can I get access access to the xml so that I can pass it to my post without it adding extra nodes to my xml?
Edit: possible solution
Doing #xml.target! seems to resolve the issue, but I'm not sure I understand why.
response = HTTParty.post(API_ENDPOINT, :body => #xml.target!)
Perhaps someone can help me understand what is happening here.
Using
puts xml
is outputting the Builder::XmlMarkup object and hence give the error
Using
puts xml.target!
outputs the current xml string, which is what you want

Yelp JSON output is not converting to ruby hash

Here is my YELP client using signet but once I get response, I'm not able to convert to ruby hash to inspect response element.
require 'rubygems'
require 'json'
require 'net/http'
client = Signet::OAuth1::Client.new(
:client_credential_key =>
'xxxxxxxxxxxxxxxxxxx',
:client_credential_secret =>
'xxxxxxxxxxxxxxxxxxx',
:token_credential_key =>
'xxxxxxxxxxxxxxxxxxx',
:token_credential_secret =>
'xxxxxxxxxxxxxxxxxxx'
)
response = client.fetch_protected_resource(
:uri => 'http://api.yelp.com/v2/search?term=food&location=san+francisco'
)
# The Rack response format is used here
status, headers, body = response
puts body["businesses"]
Error:
`[]': can't convert String into Integer (TypeError)
Body prints fine in nice JSON format but I can do body["businesses"] for instance
JSON.parse(body).inspect is also not working.
Btw body outputs itself appears as JSON format but JSON.parse(body) doesn't produce hash
puts body
{"region":{"span":{"latitude_delta":0.0,"longitude_delta":0.0},"center":{"latitude":37.660418999999997,"longitude":-121.876508}},"total":853,"businesses":[{"rating":4.0,"mobile_url":"http://m.yelp.com/biz/TT1t4oHeZmqkoiuwgCN4bQ","rating_img_url":"http://media2.ak.yelpcdn.com/static/201012164084228337/img/ico/stars/stars_4.png","review_count":150,"name":"India Garden","rating_img_url_small":"http://media2.ak.yelpcdn.com/static/20101216418129184/img/ico/stars/stars_small_4.png","url":"http://www.yelp.com/biz/india-garden-pleasanton-2","phone":"9254854800","snippet_text":"We went to this place without seeing any reviews while we returning to San Jose from Cache Creek in Brooks. This place looks like a house which was...","image_url":"http://s3-media4.ak.yelpcdn.com/bphoto/8iFj1S9YaU5IdUazwZOG8A/ms.jpg","snippet_image_url":"http://s3-media3.ak.yelpcdn.com/photo/d2TovvsTn2eUw4xqTB4jyw/ms.jpg","display_phone":"+1-925-485-4800","rating_img_url_large":"http://media4.ak.yelpcdn.com/static/20101216169592178/img/ico/stars/stars_large_4.png","id":"india-garden-pleasanton-2","categories":[["Indian","indpak"],["Pakistani","pakistani"]],"location":{"cross_streets":"Main St & Neal St","city":"Pleasanton","display_address":["210 Rose Ave","(b/t Main St & Neal St)","Pleasanton, CA 94566"],"geo_accuracy":8,"postal_code":"94566","country_code":"US","address":["210 Rose Ave"],"coordinate":{"latitude":37.660418999999997,"longitude":-121.876508},"state_code":"CA"}}]}
Actually i'm pretty sure, that body is an Array at this point, since response contains four parts and not three, so the last two parts (an array) are put into the body-object.
Also Array is the only core-object i know, which complains about an [] parameter being not an integer. If it was a string, it would try a regex/contain match.
So to sum up, body is an Array with only one Value containing a String. So to get your Hash (from JSON) you have to real_body = JSON.parse body[0]. Then you should get your hash and
real_body["businesses"] puts your businesses (the output is rather long so i will not be posting it here)
body is a string at this point, not a hash. The [] operator is complaining because the string [] operator only takes an integer, so it's trying to turn your string into an integer and failing.
edit: you can test this by just printing out body.class

Resources