Using ast.literal_eval on a nested dictionary
abstract-syntax-tree, dictionary, eval, json, python
Solution
`ast.literal_eval` has no problem with nested dictionaries:
>>> ast.literal_eval("{'a': {'b':'c'}}")
{'a': {'b': 'c'}}
`ast.literal_eval` is breaking because the data are, in fact, JSON… And JSON is not valid Python. Specifically, `null` is not a valid Python literal.
Why not just use `json.loads()` to load the data?
Problem
I'm using ast.literal_eval to change the data I receive from json.loads() into a Python dictionary; however if I should just be going about this an entirely different way - feel free to point that out as well. ``` # Authentication buf = StringIO.StringIO() c = pycurl.Curl() c.setopt(c.URL, "https://kippt.com/api/account") c.setopt(c.WRITEFUNCTION, buf.write) c.setopt(c.HTTPHEADER, header) c.perform() result = buf.getvalue() buf.close() print result # Printing Output data_string = json.dumps(result) jsonload = json.loads(data_string) jsondict = ast.literal_eval(jsonload) ``` Currently it works properly with a one line JSON return, eg: {"username": "my_username", "api_token": "my_api_token"} and I can get the values properly with: ``` print jsondict['username'] print jsondict['api_token'] ``` The part where I'm having an issue with is when the data is nested, such as: {"meta": {"next": null, "total_count": 6, "previous": null, "limit": 20, "offset": 0}, "objects": [{"rss_url": "https://kippt.com/feed/username_here/stuff_here/cool-stuff", "updated": "1339003710", "title": "Cool Stuff", "created": "1339001514", "slug": "cool-stuff", "id": 54533, "resource_uri": "/api/lists/54533/"}, {"rss_url": "https://kippt.com/feed/username_here/stuff_here/programming", "updated": "1339003479", "title": "Programming", "created": "1339001487", "slug": "programming", "id": 54532, "resource_uri": "/api/lists/54532/"}, {"rss_url": "https://kippt.com/feed/username_here/stuff_here/android", "updated": "1339003520", "title": "Android", "created": "1339000936", "slug": "android", "id": 54530, "resource_uri": "/api/lists/54530/"}, {"rss_url": "https://kippt.com/feed/username_here/stuff_here/chrome", "updated": "1339000931", "title": "Chrome", "created": "1339000412", "slug": "chrome", "id": 54529, "resource_uri": "/api/lists/54529/"}, {"rss_url": "https://kippt.com/feed/username_here/stuff_here/inbox", "updated": "1338946730", "title": "Inbox", "created": "1338945940", "slug": "inbox", "id": 54432, "resource_uri": "/api/lists/54432/"}, {"rss_url": "https://kippt.com/feed/username_here/stuff_here/read-later", "updated": "1338945940", "title": "Read Later", "created": "1338945940", "slug": "read-later", "id": 54433, "resource_uri": "/api/lists/54433/"}]} When I use the same code (Exchange URL for /api/lists) then I get the following error upon running the script: Traceback (most recent call last): File "kippt.py", line 48, in jsondict = ast.literal_eval(jsonload) File "/usr/local/lib/python2.7/ast.py", line 80, in literal_eval return _convert(node_or_string) File "/usr/local/lib/python2.7/ast.py", line 63, in _convert in zip(node.keys, node.values)) File "/usr/local/lib/python2.7/ast.py", line 62, in return dict((_convert(k), _convert(v)) for k, v File "/usr/local/lib/python2.7/ast.py", line 63, in _convert in zip(node.keys, node.values)) File "/usr/local/lib/python2.7/ast.py", line 62, in return dict((_convert(k), _convert(v)) for k, v File "/usr/local/lib/python2.7/ast.py", line 79, in _convert raise ValueError('malformed string') ValueError: malformed string Any help would be appreciated. Thanks! Edit - Answer below: Looks like my first input could have been interpretted as Python syntax, which is where my fault lay as I technically wasn't doing it the right way to begin with even with that. I now just want to json.loads() my result from cURL rather than doing the screwy stuff I was doing before. eg: ``` buf = StringIO.StringIO() c = pycurl.Curl() c.setopt(c.URL, "https://kippt.com/api/lists") c.setopt(c.WRITEFUNCTION, buf.write) c.setopt(c.HTTPHEADER, header) c.perform() result = buf.getvalue() buf.close() print result # Printing Output jsonload = json.loads(result) print jsonload['meta']['total_count'] # Gets the total_count item in the meta object. ```