Estoy usando Python 2 para analizar JSON de archivos de texto codificados ASCII .
Al cargar estos archivos con json
o simplejson
, todos mis valores de cadena se convierten en objetos Unicode en lugar de objetos de cadena. El problema es que tengo que usar los datos con algunas bibliotecas que solo aceptan objetos de cadena. No puedo cambiar las bibliotecas ni actualizarlas.
¿Es posible obtener objetos de cadena en lugar de Unicode?
Ejemplo
>>> import json
>>> original_list = ['a', 'b']
>>> json_list = json.dumps(original_list)
>>> json_list
'["a", "b"]'
>>> new_list = json.loads(json_list)
>>> new_list
[u'a', u'b'] # I want these to be of type `str`, not `unicode`
Actualizar
Esta pregunta se hizo hace mucho tiempo , cuando estaba atascado con Python 2 . Una solución fácil y limpia para hoy es usar una versión reciente de Python, es decir, Python 3 y versiones posteriores .
python
json
serialization
unicode
python-2.x
Brutus
fuente
fuente
str
Respuestas:
Una solución con
object_hook
Ejemplo de uso:
¿Cómo funciona esto y por qué lo usaría?
La función de Mark Amery es más corta y clara que estas, entonces, ¿qué sentido tienen? ¿Por qué querrías usarlos?
Puramente por su rendimiento . La respuesta de Mark decodifica el texto JSON completamente primero con cadenas unicode, luego recurre a través de todo el valor decodificado para convertir todas las cadenas en cadenas de bytes. Esto tiene un par de efectos indeseables:
Esta respuesta mitiga ambos problemas de rendimiento mediante el uso del
object_hook
parámetro dejson.load
yjson.loads
. De los documentos :Dado que los diccionarios anidan muchos niveles profundos en otros diccionarios a los que se pasan a
object_hook
medida que se decodifican , podemos byteificar cualquier cadena o lista dentro de ellos en ese punto y evitar la necesidad de una recursión profunda más adelante.La respuesta de Mark no es adecuada para su uso tal
object_hook
como está, porque se repite en diccionarios anidados. Prevenimos esa recursividad en esta respuesta con elignore_dicts
parámetro to_byteify
, que se le pasa en todo momento, excepto cuando se leobject_hook
pasa un nuevodict
para byteificar. Laignore_dicts
bandera le dice_byteify
que ignoredict
los mensajes de correo electrónico ya que han sido modificados.Finalmente, nuestras implementaciones de
json_load_byteified
andjson_loads_byteified
call_byteify
(withignore_dicts=True
) en el resultado devuelto porjson.load
ojson.loads
para manejar el caso en el que el texto JSON que se decodifica no tiene undict
nivel superior.fuente
return { byteify(key, ignore_dicts=True): _byteify(value, ignore_dicts=True) for key, value in data.iteritems() }
conreturn dict((_byteify(key, ignore_dicts=True), _byteify(value, ignore_dicts=True)) for key, value in data.iteritems())
para que funcione.json_loads_byteified('[' * 990 + ']' * 990)
. Con 991 se bloquea. Marcos todavía trabaja con 991:byteify(json.loads('[' * 991 + ']' * 991))
. Se bloquea a 992. Entonces, al menos en esta prueba, Mark puede ir más profundo, al contrario de lo que dijiste.Si bien hay algunas buenas respuestas aquí, terminé usando PyYAML para analizar mis archivos JSON, ya que proporciona las claves y los valores como
str
cadenas deunicode
tipo en lugar de tipo. Como JSON es un subconjunto de YAML, funciona bien:Notas
Algunas cosas a tener en cuenta sin embargo:
Obtengo objetos de cadena porque todas mis entradas están codificadas en ASCII . Si usara entradas codificadas Unicode, las recuperaría como objetos Unicode , ¡no hay conversión!
Debería (probablemente siempre) usar la
safe_load
función de PyYAML ; Si lo usa para cargar archivos JSON, no necesita la "potencia adicional" de laload
función de todos modos.Si desea un analizador YAML que tenga más soporte para la versión 1.2 de la especificación (y analiza correctamente números muy bajos ) intente Ruamel YAML :
pip install ruamel.yaml
yimport ruamel.yaml as yaml
fue todo lo que necesitaba en mis pruebas.Conversión
Como se dijo, no hay conversión! Si no puede estar seguro de tratar solo con valores ASCII (y no puede estar seguro la mayor parte del tiempo), mejor use una función de conversión :
Utilicé el de Mark Amery un par de veces, funciona muy bien y es muy fácil de usar. También puede usar una función similar como
object_hook
, ya que podría aumentar el rendimiento en archivos grandes. Vea la respuesta un poco más complicada de Mirec Miskuf para eso.fuente
yaml.load(json.dumps([u'a', u'£', u'É']))
en el shell de Python y observe que regresa['a', u'\xa3', u'\xc9']
(que contieneunicode
cadenas). Si no puede estar seguro de que sus datos solo contienen caracteres del conjunto de caracteres ASCII, debe usar un enfoque diferente (recomiendo mi propia respuesta).[u'a', u'b']
tener cuidado.No hay una opción integrada para hacer que las funciones del módulo json devuelvan cadenas de bytes en lugar de cadenas unicode. Sin embargo, esta función recursiva corta y simple convertirá cualquier objeto JSON descodificado del uso de cadenas unicode a cadenas de bytes codificadas en UTF-8:
Simplemente llame a esto en la salida que obtiene de una
json.load
ojson.loads
llamada.Un par de notas:
return {byteify(key): byteify(value) for key, value in input.iteritems()}
conreturn dict([(byteify(key), byteify(value)) for key, value in input.iteritems()])
, ya que las comprensiones de diccionario no fueron compatibles hasta Python 2.7.object_hook
oobject_pairs_hook
. La respuesta de Mirec Miskuf es hasta ahora la única que logra llevar esto a cabo correctamente, aunque como consecuencia, es significativamente más complicado que mi enfoque.fuente
object_hook
embargo, la respuesta a continuación usando es en realidad mucho peor que esta, pero, usandoobject_pairs_hook
, puede llegar a un método razonablemente eficiente que no requiera recurrencia o revisión de nodos que no contengan cadenas.object_pairs_hook
método es quizás un poco más difícil de entender que este (debe comprender cómo funciona el parámetro y por qué las listas y los dictados requieren un manejo diferente), y el beneficio de rendimiento no será importante para la mayoría de las personas ... pero esperaría existe, especialmente para cualquiera que esté lidiando con un objeto JSON inusualmente anidado.Puede usar el
object_hook
parámetro parajson.loads
pasar en un convertidor. No tiene que hacer la conversión después del hecho. Eljson
módulo siempre pasaráobject_hook
solo los dictados, y pasará recursivamente rectas anidadas, por lo que no tiene que recurrir a los dictados anidados usted mismo. No creo que convertiría cadenas Unicode en números como los programas de Wells. Si se trata de una cadena Unicode, se citó como una cadena en el archivo JSON, por lo que se supone que es una cadena (o el archivo es incorrecto).Además, trataría de evitar hacer algo como
str(val)
en ununicode
objeto. Debe usarvalue.encode(encoding)
una codificación válida, según lo que espere su lib externa.Así por ejemplo:
fuente
s
es un JSONObject
(una colección desordenada de clave: pares de valores con el carácter ':' que separa la clave y el valor, separados por comas y encerrados entre llaves), pero no si es, por ejemplo, un JSONArray
. Así que si se les da un JSONArray
como["a", "b"]
, el resultado seguirá siendo[u'a', u'b']
. Ninguno de los otros parámetros de tipo gancho de personalización disponibles actualmentejson.loads()
puede hacer el trabajo tampoco.json
módulo pasará recursivamentedict
s anidados , no es necesario verificarlos en las dos funciones, por lo que las doselif
cláusulas que los verifican deben eliminarse.from Utility import *
, las funciones no se verán debido a ese guión bajo.object_hook
se llama por cada objeto json analizado, por lo que si recurres a lo que se te da, estás re "byteificando" cosas que ya has "byteificado". El rendimiento crecerá geométricamente con el tamaño del objeto. He incluido una respuesta aquí que usaobject_pairs_hook
y no sufre ese problema.Eso es porque json no tiene diferencia entre los objetos de cadena y los objetos unicode. Todas son cadenas en javascript.
Creo que JSON tiene razón al devolver objetos Unicode . De hecho, no aceptaría nada menos, ya que las cadenas de JavaScript son en realidad
unicode
objetos (es decir, las cadenas JSON (javascript) pueden almacenar cualquier tipo de carácter unicode), por lo que tiene sentido crearunicode
objetos al traducir cadenas de JSON. Las cadenas simples simplemente no encajarían, ya que la biblioteca tendría que adivinar la codificación que desea.Es mejor usar
unicode
objetos de cadena en todas partes. Por lo tanto, su mejor opción es actualizar sus bibliotecas para que puedan manejar objetos Unicode.Pero si realmente desea cadenas de bytes, simplemente codifique los resultados a la codificación de su elección:
fuente
Existe una solución fácil.
TL; DR: uso en
ast.literal_eval()
lugar dejson.loads()
. Ambosast
yjson
están en la biblioteca estándar.Si bien no es una respuesta "perfecta", se obtiene bastante lejos si su plan es ignorar Unicode por completo. En Python 2.7
da:
Esto se vuelve más peludo cuando algunos objetos son realmente cadenas Unicode. La respuesta completa se vuelve peluda rápidamente.
fuente
null
,true
ofalse
valores, porque no son válidos en python y causaránliteral_eval()
un error.\/
) dentro de una cadena, o una secuencia de escape unicode (como"\u0061"
, que es otra forma de escritura"a"
). La sintaxis literal de Python es incompatible con JSON de varias maneras, y no confiaría en esta respuesta para ningún script que no iba a tirar.json
para volcar los datos, solo useprint
si ejecuta python. Luegoast.literal_eval
trabajaLa respuesta de Mike Brennan es cercana, pero no hay razón para volver a atravesar toda la estructura. Si usa el
object_hook_pairs
parámetro (Python 2.7+):Con él, recibe cada objeto JSON, por lo que puede hacer la decodificación sin necesidad de recurrencia:
Tenga en cuenta que nunca tengo que llamar al gancho de forma recursiva ya que cada objeto será entregado al gancho cuando use el
object_pairs_hook
. Debes preocuparte por las listas, pero como puedes ver, un objeto dentro de una lista se convertirá correctamente y no tienes que recurrir para que suceda.EDITAR: Un compañero de trabajo señaló que Python2.6 no tiene
object_hook_pairs
. Todavía puede usar este Python2.6 haciendo un cambio muy pequeño. En el gancho de arriba, cambie:a
Luego use en
object_hook
lugar deobject_pairs_hook
:El uso de
object_pairs_hook
resultados en un diccionario menos que se instancia para cada objeto en el objeto JSON, que, si estaba analizando un documento enorme, podría valer la pena.fuente
deunicodify_hook
que exhibes en esta respuesta? Por el momento, tiene una implementacióndeunicodify_hook
que no itera sobre las listas y desunicodifica las cadenas y las listas dentro de ellas, y por lo tanto, el resultado que está exhibiendo no coincide con el resultado que su gancho realmente producirá. Arregla eso, y esta respuesta será superior a la mía.object_pairs_hook
solo se solicitan objetos , si su texto JSON tiene una lista de cadenas en el nivel superior, esta solución fallará. No hay forma de arreglar esto sin invocar alguna función en la cosa devueltajson.load
; ninguno de losjson.load
ganchos puede garantizar que podrá lidiar con cada cadena. Creo que este es un defecto lo suficientemente grande como para seguir recomendando mi solución en lugar de usar los ganchos.Me temo que no hay forma de lograr esto automáticamente dentro de la biblioteca simplejson.
El escáner y el decodificador en simplejson están diseñados para producir texto unicode. Para hacer esto, la biblioteca usa una función llamada
c_scanstring
(si está disponible, por velocidad), opy_scanstring
si la versión C no está disponible. Lascanstring
función se llama varias veces por casi todas las rutinas que tiene simplejson para decodificar una estructura que puede contener texto. Tendría que monopatch elscanstring
valor en simplejson.decoder, o subclaseJSONDecoder
y proporcionar prácticamente su propia implementación completa de cualquier cosa que pueda contener texto.Sin embargo, la razón por la que simplejson genera unicode es que la especificación json menciona específicamente que "Una cadena es una colección de cero o más caracteres Unicode" ... se supone que el soporte para unicode forma parte del formato en sí. La
scanstring
implementación de Simplejson va tan lejos como para escanear e interpretar escapes unicode (incluso la comprobación de errores para representaciones de caracteres de múltiples bytes con formato incorrecto), por lo que la única forma en que puede devolverle el valor de manera confiable es como unicode.Si tiene una biblioteca antigua que necesita un archivo
str
, le recomiendo que busque laboriosamente en la estructura de datos anidados después del análisis (lo que reconozco es lo que dijo explícitamente que quería evitar ... lo siento), o tal vez incluya sus bibliotecas en algún tipo de fachada donde puede masajear los parámetros de entrada a un nivel más granular. El segundo enfoque podría ser más manejable que el primero si sus estructuras de datos están realmente anidadas.fuente
Como Mark (Amery) señala correctamente: El uso del deserializador de PyYaml en un volcado json solo funciona si solo tiene ASCII. Al menos fuera de la caja.
Dos comentarios rápidos sobre el enfoque PyYaml:
NUNCA use yaml.load en datos del campo. Es una característica (!) De yaml para ejecutar código arbitrario oculto dentro de la estructura.
Usted puede hacer que funcione también para no ASCII a través de este:
Pero el rendimiento no es comparable con la respuesta de Mark Amery:
Al arrojar algunos dictados de muestra profundamente anidados en los dos métodos, obtengo esto (con dt [j] = time delta de json.loads (json.dumps (m))):
Entonces, la deserialización incluye caminar completamente por el árbol y codificar, dentro del orden de magnitud de la implementación basada en C de json. Encuentro esto notablemente rápido y también es más robusto que la carga yaml en estructuras profundamente anidadas. Y menos propenso a errores de seguridad, mirando yaml.load.
=> Aunque agradecería un puntero a un convertidor basado en C, la función byteify debería ser la respuesta predeterminada.
Esto es especialmente cierto si su estructura json es del campo, que contiene la entrada del usuario. Porque entonces probablemente necesite caminar de todos modos sobre su estructura, independientemente de sus estructuras de datos internos deseadas ('sandwich unicode' o cadenas de bytes solamente).
¿Por qué?
Normalización Unicode . Para los que no saben: tome un analgésico y lea esto .
Entonces, usando la recurrencia byteify, matas dos pájaros de un tiro:
En mis pruebas resultó que reemplazar el input.encode ('utf-8') con unicodedata.normalize ('NFC', input) .encode ('utf-8') fue incluso más rápido que sin NFC, pero eso depende en gran medida de los datos de la muestra, supongo.
fuente
El Gotcha es que
simplejson
yjson
son dos módulos diferentes, al menos en la forma que se ocupan de Unicode. Tienesjson
en py 2.6+, y esto te da valores unicode, mientras quesimplejson
devuelve objetos de cadena. Simplemente intente easy_install-ing simplejson en su entorno y vea si eso funciona. Lo hizo por mi.fuente
Simplemente use pickle en lugar de json para volcar y cargar, así:
La salida que produce es (las cadenas y los enteros se manejan correctamente):
fuente
safe_load
en YAML, no sé si existe algo similar para el pepinillo .Entonces, me he encontrado con el mismo problema. Adivina cuál fue el primer resultado de Google.
Como necesito pasar todos los datos a PyGTK, las cadenas Unicode tampoco me son muy útiles. Entonces tengo otro método de conversión recursivo. En realidad, también es necesario para la conversión JSON de typesafe: json.dump () rescataría cualquier no literal, como los objetos de Python. Sin embargo, no convierte índices dict.
fuente
Tenía un JSON dict como una cadena. Las claves y los valores eran objetos unicode como en el siguiente ejemplo:
Podría usar la
byteify
función sugerida anteriormente al convertir la cadena a undict
objeto usandoast.literal_eval(myStringDict)
.fuente
{u'key':u'value'}
no es JSONAdmite Python2 y 3 usando hook (desde https://stackoverflow.com/a/33571117/558397 )
Devoluciones:
fuente
Esto es tarde para el juego, pero construí este lanzador recursivo. Funciona para mis necesidades y creo que es relativamente completo. Te puede ayudar.
Simplemente páselo como un objeto JSON así:
Lo tengo como miembro privado de una clase, pero puede reutilizar el método como mejor le parezca.
fuente
json.loads
primero se necesita una llamada), intenta arbitrariamente convertir cadenas en ints sin razón explicada, y no es copiar y ... pegar listoReescribí _parse_json () de Wells para manejar casos en los que el objeto json en sí mismo es una matriz (mi caso de uso).
fuente
Aquí hay un codificador recursivo escrito en C: https://github.com/axiros/nested_encode
Sobrecarga de rendimiento para estructuras "promedio" alrededor del 10% en comparación con json.loads
utilizando esta estructura de prueba:
fuente
Con Python 3.6, a veces todavía me encuentro con este problema. Por ejemplo, al obtener una respuesta de una API REST y cargar el texto de respuesta en JSON, sigo obteniendo las cadenas Unicode. Encontré una solución simple usando json.dumps ().
fuente
También me encontré con este problema, y al tener que lidiar con JSON, se me ocurrió un pequeño bucle que convierte las claves Unicode en cadenas. (
simplejson
en GAE no devuelve claves de cadena).obj
es el objeto decodificado de JSON:kwargs
es lo que paso al constructor de la aplicación GAE (que no le gustan lasunicode
claves**kwargs
)No es tan robusto como la solución de Wells, pero es mucho más pequeño.
fuente
He adaptado el código de la respuesta de Mark Amery , particularmente para deshacerme de
isinstance
los pros del tipeo.La codificación se realiza manualmente y
ensure_ascii
está deshabilitada. La documentación de Pythonjson.dump
dice queDescargo de responsabilidad: en el doctest utilicé el idioma húngaro. Algunas codificaciones de caracteres relacionadas con Hungría son:
cp852
la codificación IBM / OEM utilizada, por ejemplo. en DOS (a veces denominado ascii , incorrectamente creo que depende de la configuración de la página de códigos ),cp1250
por ejemplo , se usa. en Windows (a veces denominado ansi , depende de la configuración regional) yiso-8859-2
, a veces, se usa en servidores http. El texto de la pruebaTüskéshátú kígyóbűvölő
se atribuye a Koltai László (formulario de nombre personal nativo) y es de wikipedia .También me gustaría destacar la respuesta de Jarret Hardie que hace referencia a la especificación JSON , citando:
En mi caso de uso, tenía archivos con json. Son
utf-8
archivos codificados.ensure_ascii
da como resultado archivos json que escapan correctamente pero no son muy legibles, es por eso que he adaptado la respuesta de Mark Amery para satisfacer mis necesidades.El documento no es particularmente atento, pero comparto el código con la esperanza de que sea útil para alguien.
fuente
json.loads
serán listas o dictados, no algún tipo definido por el usuario o por la biblioteca que implemente sus métodos y métodos mágicos, entonces, ¿por qué no hacer unaisinstance
verificación? ¿No es más fácil de entender que verificar la existenciaiteritems
o siiter
aceptará el objeto como argumento?Mira esta respuesta a una pregunta similar como esta que dice que
El prefijo u solo significa que tiene una cadena Unicode. Cuando realmente use la cadena, no aparecerá en sus datos. No se deje llevar por la salida impresa.
Por ejemplo, intente esto:
No verás una u.
fuente
'{}'.format({u'x' : u'y'})
todavía incluye los u.