180

So I've spent way to much time on this, and it seems to me like it should be a simple fix. I'm trying to use Facebook's Authentication to register users on my site, and I'm trying to do it server side. I've gotten to the point where I get my access token, and when I go to:

https://graph.facebook.com/me?access_token=MY_ACCESS_TOKEN

I get the information I'm looking for as a string that's like this:

{"id":"123456789","name":"John Doe","first_name":"John","last_name":"Doe","link":"http:\/\/www.facebook.com\/jdoe","gender":"male","email":"jdoe\u0040gmail.com","timezone":-7,"locale":"en_US","verified":true,"updated_time":"2011-01-12T02:43:35+0000"}

It seems like I should just be able to use dict(string) on this but I'm getting this error:

ValueError: dictionary update sequence element #0 has length 1; 2 is required

So I tried using Pickle, but got this error:

KeyError: '{'

I tried using django.serializers to de-serialize it but had similar results. Any thoughts? I feel like the answer has to be simple, and I'm just being stupid. Thanks for any help!

LunaCodeGirl
  • 5,432
  • 6
  • 30
  • 36
  • If you want to eval the string as Python, you may need to change your string: `"verified":true` fails unless `true` is defined. Or you could use `"verified":True`, or `"verified":"true"`. – Matt Curtis Feb 06 '11 at 23:55
  • 2
    @Matt: I doubt he can change graph.facebook.com's output format. – Fred Nurk Feb 06 '11 at 23:58
  • @Fred: Given the title of the question ("String to Dictionary in Python"), I guess he could change it from Python before he calls `ast.literal_eval()`. Your (revised) answer is right, though - a JSON deserializer is a better solution. – Matt Curtis Feb 07 '11 at 00:12
  • 1
    @MattCurtis: Changing that in a robust way (before ast.literal_eval) would require parsing it as JSON in the first place. I mentioned ast.literal_eval as the correct way to do what the OP tried to do with dict(some_string). – Fred Nurk Feb 07 '11 at 00:15
  • @Fred: I think we're agreeing to agree :-) – Matt Curtis Feb 07 '11 at 00:54
  • related: http://stackoverflow.com/questions/988228/converting-a-string-to-dictionary (no dupe though since here json can be used) – Tobias Kienzler Jul 23 '13 at 11:50
  • Fred Nurk could you please explain, why did you put the string literal of the s variable in the triple quotation marks? – acacia Oct 17 '16 at 06:52

3 Answers3

331

This data is JSON! You can deserialize it using the built-in json module if you're on Python 2.6+, otherwise you can use the excellent third-party simplejson module.

import json    # or `import simplejson as json` if on Python < 2.6

json_string = u'{ "id":"123456789", ... }'
obj = json.loads(json_string)    # obj now contains a dict of the data
Cameron
  • 96,106
  • 25
  • 196
  • 225
  • 9
    Why did you put `u` in front of your example JSON string? – John Machin Feb 07 '11 at 00:19
  • 2
    @John: It indicates a [Unicode string](http://docs.python.org/howto/unicode.html#the-unicode-type). I put it mostly just out of habit, but presumably the Facebook API can give back data with non-ASCII characters in it; in that case, the data would be encoded (probably in UTF-8), and `decode()`-ing it would yield a `unicode` string -- which is what I used in my example. Also, [this page](http://www.json.org/fatfree.html) mentions JSON is always in Unicode (search for the term, it's about halfway down) – Cameron Feb 07 '11 at 00:40
  • 3
    It indicates a small-u unicode literal in Python. Habit is not a good reason. "The character encoding of JSON text is always Unicode." -- [Uu]nicode is NOT an encoding. What json.loads() expects is what you have got "over the wire" which is typically a str object encoded in ASCII. The only case where you would feed json.loads() a unicode object intentionally is where some strange person had transmitted it in UTF-16 and as documented you need to decode it yourself. – John Machin Feb 07 '11 at 01:08
  • 1
    @John: Yes, small-u `unicode` is the Python type, which contains a Unicode (big-U proper noun) string. I also agree that Unicode is not at all an encoding, so perhaps I shouldn't be pointing to that page as a reference. There is no reason to avoid passing `unicode` strings to `json.loads`, though -- the [docs](http://docs.python.org/library/json.html#json.loads) clearly state that this is perfectly acceptable, and I like using a pre-decoded string as it's more explicit. – Cameron Feb 07 '11 at 01:21
  • 10
    @John: Sorry to be pedantic, but `json.loads()` does not expect a `str` object encoded in ASCII -- it expects either a `str` object encoded in *UTF-8* or a `unicode` object (or a `str` object plus an explicit encoding) – Cameron Feb 07 '11 at 01:43
24

Use ast.literal_eval to evaluate Python literals. However, what you have is JSON (note "true" for example), so use a JSON deserializer.

>>> import json
>>> s = """{"id":"123456789","name":"John Doe","first_name":"John","last_name":"Doe","link":"http:\/\/www.facebook.com\/jdoe","gender":"male","email":"jdoe\u0040gmail.com","timezone":-7,"locale":"en_US","verified":true,"updated_time":"2011-01-12T02:43:35+0000"}"""
>>> json.loads(s)
{u'first_name': u'John', u'last_name': u'Doe', u'verified': True, u'name': u'John Doe', u'locale': u'en_US', u'gender': u'male', u'email': u'jdoe@gmail.com', u'link': u'http://www.facebook.com/jdoe', u'timezone': -7, u'updated_time': u'2011-01-12T02:43:35+0000', u'id': u'123456789'}
Fred Nurk
  • 13,952
  • 4
  • 37
  • 63
1

In Python 3.x

import json
t_string = '{"Prajot" : 1, "Kuvalekar" : 3}'
res = json.loads(t_string)
print(res) # <dict>  {"Prajot" : 1, "Kuvalekar" : 3}
Prajot Kuvalekar
  • 5,128
  • 3
  • 21
  • 32