views:

87

answers:

2

So I'm trying to use Google Map suggest API to request place name suggestions. Unfortunately I can't find the docs for this bit.

Here is an example URI:

http://maps.google.com/maps/suggest?q=lon&cp=3&ll=55.0,-3.5&spn=11.9,1.2&hl=en&gl=uk&v=2

which returns:

{suggestion:[{query:"London",...

I want to use this in python (2.5). Now in proper JSON there would have been quotations around the keys like so:

{"suggestion":[{"query":"London",...

and I could have used simplejson or something, but as it is I'm a bit stuck.

There are two possible solutions here; either I can get to the API code and find an option to return proper JSON, or I do that in python.

Any ideas please.

+2  A: 

I would try to poke around in order to get JSON, but failing that there's this little monstrosity which someone will inevitably yell at me about:

class Iden(object):
  def __getitem__(name, index):
    return index

notjson = '{...}'

data = eval(notjson, {}, Iden())
Ignacio Vazquez-Abrams
I'm not really sure why that works, but it certainly does. Thanks.
It works because when `eval()` hits the key of `suggestion` it looks up the name in the scopes it has. The local scope is created from an `Iden` mapping, which just turns around and returns the string used for the lookup. So `suggestion` becomes `'suggestion'`, which is a perfectly valid key.
Ignacio Vazquez-Abrams
eek! You'd have to trust Google Maps pretty well to allow them to `eval` arbitrary Python code on your server! :-) Apart from that there are problems with any unquoted name that's a Python keyword or builtin, JS true/false/null are returned as strings, and as all strings are undecoded byte strings, any `\u` escapes will go wrong.
bobince
Sure, but those can be solved by making `__getitem__()` more clever.
Ignacio Vazquez-Abrams
And as for "arbitrary"... `>>> print eval('os.system("ls")', {}, Iden())` ... `AttributeError: 'str' object has no attribute 'system'`
Ignacio Vazquez-Abrams
Hiding builtins is insufficient to jail Python. One way out: `eval('(lambda _:_).func_globals["__builtins__"]["__import__"]("os").system("ls")', {}, Iden())`.
bobince
+2  A: 

Ugh, that's indeed pretty annoying. It's a JavaScript literal but it — pointlessly — isn't JSON.

In theory you are supposed to be able to import json.decoder.JSONDecoder from the Python stdlib (or simplejson pre-2.6, which is the same) and subclass it, then pass that subclass to json.loads to override decoder behaviour. In reality this isn't really feasible as json.decoder is full of global cross-references that resist subclassing, and the bit you need to change is slap bang in the middle of def JSONObject.

So it's probably worth looking at other Python JSON libraries. I found this one which, in ‘non-strict’ mode, will parse unquoted object property names:

>>> import demjson
>>> demjson.decode('{suggestion:[{query:"London",interpretation: ...')
{u'suggestion': [{u'query': u'London', u'operation': 2, u'interpretation': ...
bobince
Works just fine. Thanks.