Json.dump que falla con ‘debe ser unicode, no str’ TypeError

Tengo un archivo json que tiene una multitud de caracteres chinos y japoneses (y otros idiomas). Lo estoy cargando en mi script de Python 2.7 usando io.open siguiente manera:

 with io.open('multiIdName.json', encoding="utf-8") as json_data: cards = json.load(json_data) 

Añado una nueva propiedad al json, todo bien. Luego bash escribirlo de nuevo en otro archivo:

 with io.open("testJson.json",'w',encoding="utf-8") as outfile: json.dump(cards, outfile, ensure_ascii=False) 

Ahí es cuando recibo el error TypeError: must be unicode, not str

Intenté escribir el archivo outfile como binario ( with io.open("testJson.json",'wb') as outfile: , pero termino con cosas como esto:

 {"multiverseid": 262906, "name": "\u00e6\u00b8\u00b8\u00e9\u009a\u00bc\u00e7\u008b\u00ae\u00e9\u00b9\u00ab", "language": "Chinese Simplified"} 

Pensé que abrirlo y escribirlo en la misma encoding sería suficiente, así como la bandera de asegurar_ascii, pero claramente no. Solo quiero conservar los caracteres que existían en el archivo antes de ejecutar mi script, sin que se conviertan en \ u’s.

¿Puedes probar lo siguiente?

 with io.open("testJson.json",'w',encoding="utf-8") as outfile: outfile.write(unicode(json.dumps(cards, ensure_ascii=False))) 

El motivo de este error es el comportamiento completamente estúpido de json.dumps en Python 2:

 >>> json.dumps({'a': 'a'}, ensure_ascii=False) '{"a": "a"}' >>> json.dumps({'a': u'a'}, ensure_ascii=False) u'{"a": "a"}' >>> json.dumps({'a': 'ä'}, ensure_ascii=False) '{"a": "\xc3\xa4"}' >>> json.dumps({u'a': 'ä'}, ensure_ascii=False) Traceback (most recent call last): File "", line 1, in  File "/usr/lib/python2.7/json/__init__.py", line 250, in dumps sort_keys=sort_keys, **kw).encode(obj) File "/usr/lib/python2.7/json/encoder.py", line 210, in encode return ''.join(chunks) UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 1: ordinal not in range(128) 

Esto, junto con el hecho de que io.open con el conjunto de encoding solo acepta objetos unicode (que en sí mismo es correcto), conduce a problemas.

El tipo de retorno depende completamente de lo que sea el tipo de claves o valores en el diccionario, si ensure_ascii=False , pero str se devuelve siempre si ensure_ascii=True . Si accidentalmente puede establecer cadenas de 8 bits en los diccionarios, no puede convertir a ciegas este tipo de retorno a unicode , porque necesita configurar la encoding, probablemente UTF-8:

 >>> x = json.dumps(obj, ensure_ascii=False) >>> if isinstance(x, str): ... x = unicode(x, 'UTF-8') 

En este caso, creo que puede usar el json.dump para escribir en un archivo binario abierto; Sin embargo, si necesita hacer algo más complicado con el objeto resultante, es probable que necesite el código anterior.


Una solución es poner fin a toda esta locura de encoding / deencoding cambiando a Python 3.

El módulo JSON se encarga de la encoding y la deencoding, por lo que simplemente puede abrir los archivos de entrada y salida en modo binario. El módulo JSON asume la encoding UTF-8, pero se puede cambiar utilizando el atributo de encoding en los métodos load() y dump() .

 with open('multiIdName.json', 'rb') as json_data: cards = json.load(json_data) 

entonces:

 with open("testJson.json", 'wb') as outfile: json.dump(cards, outfile, ensure_ascii=False) 

Gracias a @Antti Haapala, el módulo JSON de Python 2.x proporciona Unicode o str en función del contenido del objeto.

Deberá agregar una verificación de sentido para asegurarse de que el resultado sea un Unicode antes de escribir a través de io :

 with io.open("testJson.json", 'w', encoding="utf-8") as outfile: my_json_str = json.dumps(my_obj, ensure_ascii=False) if isinstance(my_json_str, str): my_json_str = my_json_str.decode("utf-8") outfile.write(my_json_str)