Andrés López Ferro Andrés López Ferro - 5 months ago 12
JSON Question

How do I get JSON from Google?

I'm using HTTParty to retrieve this information from Google: http://www.google.com/ig/calculator?hl=en&q=1USD=?COP

data = HTTParty.get("http://www.google.com/ig/calculator?hl=en&q=1USD=?COP").body
=> "{lhs: \"1 U.S. dollar\",rhs: \"1\xA0901.14068 Colombian pesos\",error: \"\",icc: true}"


I want to parse the data and display it as a JSON object using
JSON.parse(data)
but it returns an error:

JSON.parse(data)
JSON::ParserError: 746: unexpected token at '{lhs: "1 U.S. dollar",rhs: "1�901.14068 Colombian pesos",error: "",icc: true}'
from /usr/lib/ruby/1.9.1/json/common.rb:148:in `parse'
from /usr/lib/ruby/1.9.1/json/common.rb:148:in `parse'
from (irb):39
from /usr/bin/irb:12:in `<main>'


I want to get that info and display it on my site to put it to use, but I can not parse it.

Answer

I've been poking around, and I think the problem is at Google's end.

The data being received is a hash in Ruby, or an object in JavaScript, and, per the spec, the keys should be quoted because they're strings. An object is wrapped in { and }, with "string": value as the format. Strings are to be wrapped in double-quotes: "string".

By preprocessing the received data to convert the key names to quoted key names, the JSON can be parsed:

require 'open-uri'
require 'json'

FIELDS = %w[lhs rhs error icc]

%w[COP USD AED YER ZAR ZMK].each do |currency|
  data = open('http://www.google.com/ig/calculator?hl=en&q=1USD=?' + currency).read
  FIELDS.each { |f| data.sub!(f, %Q["#{f}"]) }
  puts JSON[data]
end

After running, these are the hashes created by parsing the JSON:

{"lhs"=>"1 U.S. dollar", "rhs"=>"1 890.35917 Colombian pesos", "error"=>"", "icc"=>true}
{"lhs"=>"1 U.S. dollar", "rhs"=>"1 U.S. dollar", "error"=>"0", "icc"=>true}
{"lhs"=>"1 U.S. dollar", "rhs"=>"3.67290571 United Arab Emirates dirhams", "error"=>"", "icc"=>true}
{"lhs"=>"1 U.S. dollar", "rhs"=>"215.053763 Yemeni rials", "error"=>"", "icc"=>true}
{"lhs"=>"1 U.S. dollar", "rhs"=>"9.97575891 South African rands", "error"=>"", "icc"=>true}
{"lhs"=>"1 U.S. dollar", "rhs"=>"5 208.33333 Zambia kwacha", "error"=>"", "icc"=>true}

But wait, there's more....

YAML is a superset of JSON, so YAML parsers should be able to understand JSON. Throwing the received "JSON" into the YAML parser deals with the unquoted keys:

require 'open-uri'
require 'yaml'

%w[COP USD AED YER ZAR ZMK].each do |currency|
  data = open('http://www.google.com/ig/calculator?hl=en&q=1USD=?' + currency).read
  puts YAML.load(data)
end

With this output:

{"lhs"=>"1 U.S. dollar", "rhs"=>"1 890.35917 Colombian pesos", "error"=>"", "icc"=>true}
{"lhs"=>"1 U.S. dollar", "rhs"=>"1 U.S. dollar", "error"=>"0", "icc"=>true}
{"lhs"=>"1 U.S. dollar", "rhs"=>"3.67290571 United Arab Emirates dirhams", "error"=>"", "icc"=>true}
{"lhs"=>"1 U.S. dollar", "rhs"=>"215.053763 Yemeni rials", "error"=>"", "icc"=>true}
{"lhs"=>"1 U.S. dollar", "rhs"=>"9.97575891 South African rands", "error"=>"", "icc"=>true}
{"lhs"=>"1 U.S. dollar", "rhs"=>"5 208.33333 Zambia kwacha", "error"=>"", "icc"=>true}

YAML doesn't insist that a hash key be quoted. It defaults to String for things that are not obviously numeric, and that was my clue to try YAML.

So, in my opinion, Google has done the wrong thing. If they're going to output JSON, they should output normal, syntactically correct JSON. If they're going to output YAML they should output regular YAML, not this "half-fast" serializing of the data. The API might have a way of forcing it one way or the other, but it should default to JSON that parses.

You might want to look into using https://openexchangerates.org/ instead. Their example output appears to be a more-sane approach.

Comments