Is it safe to parse json with YAML.load? - ruby

I am using ruby 2.1.0
I have a json file.
For example: test.json
{
"item":[
{"apple": 1},
{"banana": 2}
]
}
Is it safe to load this file with YAML.load?
YAML.load(File.read('test.json'))
I am trying to load a file which is in either json or yaml format.

YAML can load JSON
YAML.load('{"something": "test", "other": 4 }')
=> {"something"=>"test", "other"=>4}
JSON will not be able to load YAML.
JSON.load("- something\n")
JSON::ParserError: 795: unexpected token at '- something'
There will be some obscure cases that work and produce different output.
YAML.load("")
=> false
JSON.load("")
=> nil
But generally the YAML construct is not JSON compliant.
So, try the JSON.load first because it's probably better at obscure JSON things.Catch the JSON::ParserError error and fall back to YAML.load.

In recent work I did I found a corner case of the sort alluded to by Matt. For example
puts JSON.load('{"x": "foo\/bar"}')['x']
succeeds in printing
foo/bar
despite the gratuitous escaping¹ whereas
puts YAML.load('{"x": "foo\/bar"}')['x']
fails:
Psych::SyntaxError ((<unknown>): found unknown escape character while parsing a quoted scalar at line 1 column 7)
¹In this case by Java as per net.sf.json.util.JSONUtils.quote. Note that they forgot to do the same quoting in their own Javadoc, ironically enough, so you have to browse source to understand!

Related

Specific Values in Json Parse

I am having difficulty getting to specific values when I parse a JSON file in Ruby. My JSON is based off of this link https://www.mcdonalds.com/services/mcd/us/restaurantLocator?latitude=40.7217861&longitude=-74.00944709999999&radius=8045&maxResults=100&country=us&language=en-us
No matter what I try I cannot pull the values I want, which is the "addressLine1" field. I get the following error:
`[]': no implicit conversion of String into Integer (TypeError)
Code
require 'json'
file = File.read('MCD.json')
data_hash = JSON.parse(file)
print data_hash.keys
print "\n"
print data_hash['features']['addressLine1']
data_hash['features'] is an array. Depending on what do you actually need, you might either iterate over it, or call:
data_hash['features'].first['properties']['addressLine1']
Note 'properties' there, since addressLine1 is not a direct descendant of 'features' elements.

Parsing XML document missing enclosing parent entity

I want to process an XML document that lacks an overarching enclosing entity. (Yes, that's the file I'm given. No, I didn't create it.) For example:
<DeviceInfo>
<Greeting>Crunchy bacon!</Greeting>
</DeviceInfo>
<InstantaneousDemand>
<TimeStamp>0x1c722845</TimeStamp>
</InstantaneousDemand>
<InstantaneousDemand>
<TimeStamp>0x1c72284a</TimeStamp>
</InstantaneousDemand>
When I parse the file using Nokogiri's XML method, it (predictably) only reads the first entity:
>> doc = Nokogiri::XML(File.open("x.xml"))
>> doc.children.count
=> 1
doc.text
=> "\n Crunchy bacon!\n"
I could read the file as a string and wrap a fake enclosing entity around the whole thing, but that seems heavy handed. Is there a better way to get Nokogiri to read in all the entities?
You might create a DocumentFragment rather than Document (especially taking into account that your content is actually a document fragment):
▶ doc = Nokogiri::XML::DocumentFragment.parse File.read("x.xml")
#⇒ #<Nokogiri::XML::DocumentFragment:0x14efa38 name="#document-fragment"
# ...
# #<Nokogiri::XML::Element:0x14ef68c name="InstantaneousDemand"
# ...
▶ doc.children.count
#⇒ 6
Hope it helps.

Parsing valid JSON throwing errors

I’m confused as to why this throws an error:
s = <<JSON
{"s": "This is \"valid\" JSON"}
JSON
JSON.parse(s) # => JSON::ParserError: 757: unexpected token at '{"s": "This is "valid" JSON"}'
Based on using http://jsonlint.com I can confirm that this is valid JSON, so what’s the deal? I get the feeling that I could be using %q{} here and things would be escaped properly, but I’d really rather use a heredoc here.
It turns out that Ruby supports disabling interpolation in heredocs by surrounding the opening identifier with single quotes, so in my example above, it would look like this:
s = <<'JSON'
{"s": "This is \"valid\" JSON"}
JSON
JSON.parse(s) # => {"s"=>"This is \"valid\" JSON"}

Compare Json Response to Json text File

I have a file text.json and I have an JSON HTTP response. What's a good to check if they are equal?
Here's what I have but I think there's a better solutions.
JSON.parse(response["data"]).eql?(File.read(text.json))
You need to parse both ends of your test:
JSON.parse(response["data"]).eql?(JSON.parse(File.read(text.json)))
Edit
If you want to test an array of JSONs, and you are not sure that the order will be the same in the file meaning [{a:1, b:2}, {a:2, b:1}] should equal [{a:2, b:1}, {a:1, b:2}], you'll need to sort them first (see here for more techniques):
JSON.parse(response["data"]).sort.eql?(JSON.parse(File.read(text.json)).sort)
Edit 2
Since Hashes don't sort well, the above won't work. You could use one of the other techniques:
from_response = JSON.parse(response["data"])
from_file = JSON.parse(File.read(text.json))
(from_response & from_file) == from_response
(from_response - from_file).empty?

Converting Jsonp to Json in different methods

I been trying to use JSONP data in a json format in a ruby project.
From your experiences how did you address this?
JSONP is easy to handle. It's just JSON in a minor wrapper, and that wrapper is easy to strip off:
require 'open-uri'
require 'json'
URL = 'http://www.google.com/dictionary/json?callback=a&sl=en&tl=en&q=epitome'
jsonp = open(URL).read
jsonp now contains the result in JSONP format:
jsonp[0, 3] # => "a({"
jsonp[-11 ... -1] # => "},200,null"
Those extraneous parts, a{ and ,200,null" are the trouble spots when passing the data to JSON for parsing, so we strip them.
A simple, greedy, regex is all that's needed. /{.+}/ will find everything wrapped by the outermost curly-braces and return it, which is all the JSON needs:
data = JSON.parse(jsonp[/{.+}/])
data['query'] # => "epitome"
data['primaries'].size # => 1
From my experience, one way is to use this regex to filter out the function callback name:
/(\{.*\})/m
or the lazy way would be find the index of the first occurrence of "(" and just substring it with last character, which would be a ")" .
I been trying to look for answers on here, didn't get a solid answer, hope this helps.
Cheers

Resources