Cadena al diccionario en Python

126

Así que he dedicado mucho tiempo a esto, y me parece que debería ser una solución simple. Estoy tratando de usar la autenticación de Facebook para registrar usuarios en mi sitio, y estoy tratando de hacerlo desde el lado del servidor. He llegado al punto donde obtengo mi token de acceso y cuando voy a:

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

Recibo la información que busco como una cadena que es así:

{"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"}

Parece que debería poder usar dict(string)esto, pero recibo este error:

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

Así que intenté usar Pickle, pero obtuve este error:

KeyError: '{'

Intenté usar django.serializerspara deserializarlo pero obtuve resultados similares. ¿Alguna idea? Siento que la respuesta tiene que ser simple, y solo estoy siendo estúpida. ¡Gracias por cualquier ayuda!

LunaCodeGirl
fuente
Si desea evaluar la cadena como Python, es posible que deba cambiar su cadena: "verified":truefalla a menos que trueesté definida. O podrías usar "verified":True, o "verified":"true".
Matt Curtis
2
@ Matt: dudo que pueda cambiar el formato de salida de graph.facebook.com.
Fred Nurk
@Fred: Dado el título de la pregunta ("Cadena al diccionario en Python"), creo que podría cambiarlo de Python antes de llamar ast.literal_eval(). Sin embargo, su respuesta (revisada) es correcta: un deserializador JSON es una mejor solución.
Matt Curtis
1
@MattCurtis: Cambiar eso de una manera robusta (antes de ast.literal_eval) requeriría analizarlo como JSON en primer lugar. Mencioné ast.literal_eval como la forma correcta de hacer lo que el OP intentó hacer con dict (some_string).
Fred Nurk
@Fred: Creo que estamos de acuerdo :-)
Matt Curtis

Respuestas:

238

¡Estos datos son JSON ! Puede deserializarlo usando el jsonmódulo incorporado si está en Python 2.6+, de lo contrario puede usar el excelente simplejsonmódulo de terceros .

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
fuente
55
¿Por qué pusiste udelante de tu cadena JSON de ejemplo?
John Machin
2
@John: indica una cadena Unicode . Lo pongo principalmente por costumbre, pero presumiblemente la API de Facebook puede devolver datos con caracteres no ASCII; en ese caso, los datos se codificarían (probablemente en UTF-8), y decode()al producirse una unicodecadena, que es lo que usé en mi ejemplo. Además, esta página menciona que JSON siempre está en Unicode (busque el término, está a medio camino)
Cameron
3
Indica un literal unicode small-u en Python. El hábito no es una buena razón. "La codificación de caracteres del texto JSON siempre es Unicode". - [Uu] nicode NO es una codificación. Lo que json.loads () espera es lo que tiene "por cable", que normalmente es un objeto str codificado en ASCII. El único caso en el que alimentaría a json.loads () un objeto unicode intencionalmente es donde alguna persona extraña lo había transmitido en UTF-16 y, según lo documentado, debe decodificarlo usted mismo.
John Machin
1
@John: Sí, small-u unicodees el tipo de Python, que contiene una cadena Unicode (nombre propio big-U). También estoy de acuerdo en que Unicode no es en absoluto una codificación, por lo que tal vez no debería apuntar a esa página como referencia. Sin embargo, no hay ninguna razón para evitar pasar unicodecadenas json.loads: los documentos establecen claramente que esto es perfectamente aceptable, y me gusta usar una cadena predescodificada, ya que es más explícita.
Cameron
8
@John: Perdón por ser pedante, pero json.loads()no espera un strobjeto codificado en ASCII; espera un strobjeto codificado en UTF-8 o un unicodeobjeto (o un strobjeto más una codificación explícita)
Cameron
19

Use ast.literal_eval para evaluar literales de Python. Sin embargo, lo que tienes es JSON (nota "verdadero" por ejemplo), así que usa un deserializador JSON.

>>> 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'[email protected]', 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
fuente