¿Cómo obtener objetos de cadena en lugar de Unicode desde JSON?


Estoy usando Python 2para analizar JSON desde archivos de texto codificados en ASCII.

Al cargar estos archivos con cualquiera 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 string. Yo no puedo cambiar las bibliotecas ni actualizarlas.

¿Es posible obtener objetos string en lugar de Unicode ¿unos?

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`

Actualización

Esta pregunta se hizo hace mucho tiempo, cuando estaba atrapado 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 adelante.

Author: Brutus, 2009-06-05

21 answers

Una solución con object_hook

import json

def json_load_byteified(file_handle):
    return _byteify(
        json.load(file_handle, object_hook=_byteify),
        ignore_dicts=True
    )

def json_loads_byteified(json_text):
    return _byteify(
        json.loads(json_text, object_hook=_byteify),
        ignore_dicts=True
    )

def _byteify(data, ignore_dicts = False):
    # if this is a unicode string, return its string representation
    if isinstance(data, unicode):
        return data.encode('utf-8')
    # if this is a list of values, return list of byteified values
    if isinstance(data, list):
        return [ _byteify(item, ignore_dicts=True) for item in data ]
    # if this is a dictionary, return dictionary of byteified keys and values
    # but only if we haven't already byteified it
    if isinstance(data, dict) and not ignore_dicts:
        return {
            _byteify(key, ignore_dicts=True): _byteify(value, ignore_dicts=True)
            for key, value in data.iteritems()
        }
    # if it's anything else, return it in its original form
    return data

Ejemplo de uso:

>>> json_loads_byteified('{"Hello": "World"}')
{'Hello': 'World'}
>>> json_loads_byteified('"I am a top-level string"')
'I am a top-level string'
>>> json_loads_byteified('7')
7
>>> json_loads_byteified('["I am inside a list"]')
['I am inside a list']
>>> json_loads_byteified('[[[[[[[["I am inside a big nest of lists"]]]]]]]]')
[[[[[[[['I am inside a big nest of lists']]]]]]]]
>>> json_loads_byteified('{"foo": "bar", "things": [7, {"qux": "baz", "moo": {"cow": ["milk"]}}]}')
{'things': [7, {'qux': 'baz', 'moo': {'cow': ['milk']}}], 'foo': 'bar'}
>>> json_load_byteified(open('somefile.json'))
{'more json': 'from a file'}

¿Cómo funciona esto y por qué lo usaría?

La función de Mark Amery es más corta y clara que estas, así que ¿cuál es el punto de ellas? ¿Por qué querrías usarlos?

Puramente para rendimiento. La respuesta de Mark decodifica completamente el texto JSON primero con cadenas unicode, luego se repite a través de todo el valor decodificado para convertir todas las cadenas en cadenas de bytes. Esto tiene un pareja de reacciones adversas:

  • Se crea una copia de toda la estructura decodificada en memoria
  • Si tu objeto JSON está realmente profundamente anidado (500 niveles o más) entonces alcanzarás la profundidad de recursión máxima de Python

Esta respuesta mitiga ambos problemas de rendimiento mediante el uso del parámetro object_hook de json.load y json.loads. De los documentos :

object_hook es una función opcional que se llamará con el resultado de cualquier objeto literal decodificado (a dict). El valor devuelto de object_hook se utilizará en lugar de dict. Esta característica se puede utilizar para implementar decodificadores personalizados

Dado que los diccionarios anidados muchos niveles profundos en otros diccionarios se pasan a object_hook a medida que se decodifican, podemos byteificar cualquier cadena o lista dentro de ellos en ese momento y evitar la necesidad de recursión profunda más adelante.

La respuesta de Mark no es adecuada para su uso como object_hook tal como está, porque se repite en diccionarios anidados. Evitamos esa recursión en esta respuesta con el parámetro ignore_dicts a _byteify, que se le pasa en todo momento excepto cuando object_hook le pasa un nuevo dict a byteify. La bandera ignore_dicts le dice a _byteifyque ignore dict s ya que ya han sido byteificados.

Finalmente, nuestras implementaciones de json_load_byteified y json_loads_byteified llaman a _byteify (con ignore_dicts=True) en el resultado devuelto de json.load o json.loads para manejar el caso en el que el texto JSON que se decodifica no tiene un dict nivel superior.

 80
Author: Mirec Miskuf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:47:14

Si bien hay algunas buenas respuestas aquí, terminé usando PyYAML para analizar mis archivos JSON, ya que da las claves y los valores como str cadenas de tipo en lugar de unicode tipo. Debido a que JSON es un subconjunto de YAML, funciona muy bien:

>>> import json
>>> import yaml
>>> list_org = ['a', 'b']
>>> list_dump = json.dumps(list_org)
>>> list_dump
'["a", "b"]'
>>> json.loads(list_dump)
[u'a', u'b']
>>> yaml.safe_load(list_dump)
['a', 'b']

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 unicode objects - ¡no hay conversión!

  • Deberías (probablemente siempre) usar la función safe_load de PyYAML; si la usas para cargar archivos JSON, no necesitas la "potencia adicional" de la función load 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 y import ruamel.yaml as yaml era todo lo que necesitaba en mi prueba.

Conversión

Como se ha dicho, ¡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 :

Usé el de Mark Amery un par de veces ahora, funciona muy bien y es muy fácil de usar. También puede utilizar una función similar como object_hook en su lugar, ya que podría ganar un aumento de rendimiento en archivos grandes. Ver los un poco más involucrados respuesta de Mirec Miskuf para eso.

 164
Author: Brutus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:54:53

No hay una opción incorporada 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 decodificado de usar cadenas unicode a cadenas de bytes codificadas en UTF-8:

def byteify(input):
    if isinstance(input, dict):
        return {byteify(key): byteify(value)
                for key, value in input.iteritems()}
    elif isinstance(input, list):
        return [byteify(element) for element in input]
    elif isinstance(input, unicode):
        return input.encode('utf-8')
    else:
        return input

Simplemente llame a esto en la salida que obtiene de una llamada json.load o json.loads.

Un par de notas:

  • Para admitir Python 2.6 o anterior, reemplace return {byteify(key): byteify(value) for key, value in input.iteritems()} por return dict([(byteify(key), byteify(value)) for key, value in input.iteritems()]), ya que las comprensiones del diccionario no eran soportado hasta Python 2.7.
  • Dado que esta respuesta se repite a través de todo el objeto decodificado, tiene un par de características de rendimiento indeseables que se pueden evitar con un uso muy cuidadoso de los parámetros object_hook o object_pairs_hook. La respuesta de Mirec Miskuf es hasta ahora la única que logra sacar esto adelante correctamente, aunque como consecuencia, es significativamente más complicado que mi enfoque.
 137
Author: Mark Amery,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 10:31:26

Puede usar el parámetro object_hook para json.loads para pasar en un convertidor. No tienes que hacer la conversión después del hecho. Las json module siempre pasará solo los dictados object_hook, y pasará recursivamente los dictados anidados, por lo que no tendrá que recurrir a dictados anidados usted mismo. No creo que convierta cadenas unicode a números como muestra Wells. Si es 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 malo).

También, trataría de evitar hacer algo como str(val) en un objeto unicode. Debe usar value.encode(encoding) con una codificación válida, dependiendo de lo que su lib externa espera.

Así, por ejemplo:

def _decode_list(data):
    rv = []
    for item in data:
        if isinstance(item, unicode):
            item = item.encode('utf-8')
        elif isinstance(item, list):
            item = _decode_list(item)
        elif isinstance(item, dict):
            item = _decode_dict(item)
        rv.append(item)
    return rv

def _decode_dict(data):
    rv = {}
    for key, value in data.iteritems():
        if isinstance(key, unicode):
            key = key.encode('utf-8')
        if isinstance(value, unicode):
            value = value.encode('utf-8')
        elif isinstance(value, list):
            value = _decode_list(value)
        elif isinstance(value, dict):
            value = _decode_dict(value)
        rv[key] = value
    return rv

obj = json.loads(s, object_hook=_decode_dict)
 73
Author: Mike Brennan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-08-29 17:33:12

Esto se debe a que json no tiene diferencia entre los objetos string y los objetos unicode. Son todas cadenas en javascript.

Creo que JSON tiene razón al devolver objetos unicode. De hecho, no aceptaría nada menos, ya que las cadenas javascript son de hecho unicode objetos (es decir, las cadenas JSON (javascript) pueden almacenar cualquier tipo de carácter unicode), por lo que tiene sentido crear unicode objetos al traducir cadenas desde JSON. Cuerdas simples simplemente no encajarían desde la biblioteca tendría que adivinar la codificación que desee.

Es mejor usar unicode objetos string en todas partes. Así que su mejor opción es actualizar sus bibliotecas para que puedan tratar con objetos unicode.

Pero si realmente quieres bytestrings, simplemente codifica los resultados a la codificación de tu elección:

>>> nl = json.loads(js)
>>> nl
[u'a', u'b']
>>> nl = [s.encode('utf-8') for s in nl]
>>> nl
['a', 'b']
 37
Author: nosklo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-05 18:29:06

Existe una solución fácil.

TL; DR-Use ast.literal_eval() en lugar de json.loads(). Tanto ast como json están en la biblioteca estándar.

Si bien no es una respuesta 'perfecta', llega bastante lejos si su plan es ignorar Unicode por completo. En Python 2.7

import json, ast
d = { 'field' : 'value' }
print "JSON Fail: ", json.loads(json.dumps(d))
print "AST Win:", ast.literal_eval(json.dumps(d))

Da:

JSON Fail:  {u'field': u'value'}
AST Win: {'field': 'value'}

Esto se vuelve más peludo cuando algunos objetos son realmente cadenas Unicode. La respuesta completa se pone peluda rápidamente.

 14
Author: Charles Merriam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-07 01:01:43

Me temo que no hay manera 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 utiliza una función llamada c_scanstring (si está disponible, para la velocidad), o py_scanstring si la versión C no está disponible. La función scanstring es llamada varias veces por casi todas las rutinas que tiene simplejson para decodificar una estructura que podría contener texto. Usted tendría que o bien monkeypatch el valor scanstring en simplejson.decodificador, o subclase JSONDecoder y proporcionar prácticamente su propia implementación completa de cualquier cosa que pueda contener texto.

La razón por la que simplejson produce unicode, sin embargo, es que la especificación json menciona específicamente que "Una cadena es una colección de cero o más caracteres Unicode"... el soporte para unicode se asume como parte del formato en sí. La implementación de Simplejson scanstring va tan lejos como para escanear e interpretar los escapes unicode (incluso la comprobación de errores para representaciones malformadas de conjuntos de caracteres de varios bytes), por lo que la única forma en que puede devolverle el valor de forma fiable es como unicode.

Si tiene una biblioteca antigua que necesita un str, le recomiendo que busque laboriosamente la estructura de datos anidada después de analizar (lo que reconozco es lo que dijo explícitamente que quería evitar... lo sentimos), o tal vez envuelva sus bibliotecas en algún tipo de fachada donde pueda 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 profundamente anidadas.

 9
Author: Jarret Hardie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-05 18:10:03

La respuesta de Mike Brennan está cerca, pero no hay razón para volver a atravesar toda la estructura. Si utiliza el object_hook_pairs (Python 2.7+) parámetro:

object_pairs_hook es una función opcional que se llamará con el resultado de cualquier objeto decodificado literal con una lista ordenada de pares. El valor devuelto de object_pairs_hook se utilizará en lugar del dict. Esta característica se puede utilizar para implementar decodificadores personalizados que se basan en el orden en que la clave y los pares de valores se decodifican (por ejemplo, collections.OrderedDict recordará el orden de inserción). Si object_hook se define también, la object_pairs_hook tiene prioridad.

Con él, obtiene cada objeto JSON entregado a usted, por lo que puede hacer la decodificación sin necesidad de recursión:

def deunicodify_hook(pairs):
    new_pairs = []
    for key, value in pairs:
        if isinstance(value, unicode):
            value = value.encode('utf-8')
        if isinstance(key, unicode):
            key = key.encode('utf-8')
        new_pairs.append((key, value))
    return dict(new_pairs)

In [52]: open('test.json').read()
Out[52]: '{"1": "hello", "abc": [1, 2, 3], "def": {"hi": "mom"}, "boo": [1, "hi", "moo", {"5": "some"}]}'                                        

In [53]: json.load(open('test.json'))
Out[53]: 
{u'1': u'hello',
 u'abc': [1, 2, 3],
 u'boo': [1, u'hi', u'moo', {u'5': u'some'}],
 u'def': {u'hi': u'mom'}}

In [54]: json.load(open('test.json'), object_pairs_hook=deunicodify_hook)
Out[54]: 
{'1': 'hello',
 'abc': [1, 2, 3],
 'boo': [1, 'hi', 'moo', {'5': 'some'}],
 'def': {'hi': 'mom'}}

Observe que nunca tengo que llamar al hook recursivamente ya que cada objeto será entregado al hook cuando use el object_pairs_hook. Usted tiene que preocuparse por las listas, pero como se puede ver, un objeto dentro 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 se puede utilizar esta voluntad Python2. 6 haciendo un cambio muy pequeño. En el gancho de arriba, cambiar:

for key, value in pairs:

A

for key, value in pairs.iteritems():

Luego use object_hook en lugar de object_pairs_hook:

In [66]: json.load(open('test.json'), object_hook=deunicodify_hook)
Out[66]: 
{'1': 'hello',
 'abc': [1, 2, 3],
 'boo': [1, 'hi', 'moo', {'5': 'some'}],
 'def': {'hi': 'mom'}}

El uso de object_pairs_hook da como resultado la creación de una instancia de un diccionario menos para cada objeto en el objeto JSON, que, si estuviera analizando un documento de gran tamaño, podría valer la pena.

 9
Author: Travis Jensen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:34:33

Como señala Mark (Amery) correctamente: Usar el 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:

  1. NUNCA use yaml.carga los datos del campo. Es una característica (!) de yaml para ejecutar código arbitrario oculto dentro de la estructura.

  2. Usted puede hacer que funcione también para no ASCII a través de esto:

    def to_utf8(loader, node):
        return loader.construct_scalar(node).encode('utf-8')
    yaml.add_constructor(u'tag:yaml.org,2002:str', to_utf8)
    

Pero el rendimiento sabio su de ninguna comparación a la respuesta de Mark Amery:

Lanzando algunos dictados de muestra profundamente anidados en los dos métodos, obtengo esto (con dt[j] = delta de tiempo de json.cargas(json.vertederos(m))):

     dt[yaml.safe_load(json.dumps(m))] =~ 100 * dt[j]
     dt[byteify recursion(Mark Amery)] =~   5 * dt[j]

Así que deserialización incluyendo caminar completamente el árbol y codificación, bien 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 propensos a errores de seguridad, mirando yaml.carga.

=> Aunque apreciaría un puntero a un convertidor basado solo 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 internas deseadas ('unicode sandwich' o solo cadenas de bytes).

¿Por qué?

Unicode normalización . Para el inconsciente: Tome un analgésico y lea esto .

Así que usando la recursión byteify matas dos pájaros de un tiro:

  1. obtenga sus bytestrings de vertederos json anidados
  2. normaliza los valores de entrada del usuario, para que encuentres las cosas en tu almacenamiento.

En mis pruebas resultó que la sustitución de la entrada.codificar ('utf-8') con un unicodedata.normalizar('NFC", entrada).encode ('utf-8') fue incluso más rápido que sin NFC, pero eso depende en gran medida de la muestra data, supongo.

 4
Author: Red Pill,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-14 17:42:03

La conclusión es que simplejson y json son dos módulos diferentes, al menos en la forma en que tratan con unicode. Tienes json en py 2.6+, y esto te da valores unicode, mientras que simplejson devuelve objetos string. Simplemente pruebe easy_install-ing simplejson en su entorno y vea si funciona. Lo hizo por mí.

 3
Author: ducu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-10-19 19:48:34

Por lo tanto, me he encontrado con el mismo problema. Adivina cuál fue el primer resultado de Google.

Debido a que necesito pasar todos los datos a PyGTK, las cadenas unicode tampoco son muy útiles para mí. Así que tengo otro método de conversión recursivo. En realidad también es necesario para la conversión JSON typesafe-json.dump () caería sobre cualquier objeto no literal, como los objetos Python. Sin embargo, no convierte índices dict.

# removes any objects, turns unicode back into str
def filter_data(obj):
        if type(obj) in (int, float, str, bool):
                return obj
        elif type(obj) == unicode:
                return str(obj)
        elif type(obj) in (list, tuple, set):
                obj = list(obj)
                for i,v in enumerate(obj):
                        obj[i] = filter_data(v)
        elif type(obj) == dict:
                for i,v in obj.iteritems():
                        obj[i] = filter_data(v)
        else:
                print "invalid object in data, converting to string"
                obj = str(obj) 
        return obj
 1
Author: mario,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-07-05 18:22:51

Simplemente use pickle en lugar de json para volcar y cargar, así:

    import json
    import pickle

    d = { 'field1': 'value1', 'field2': 2, }

    json.dump(d,open("testjson.txt","w"))

    print json.load(open("testjson.txt","r"))

    pickle.dump(d,open("testpickle.txt","w"))

    print pickle.load(open("testpickle.txt","r"))

La salida que produce es (las cadenas y los enteros se manejan correctamente):

    {u'field2': 2, u'field1': u'value1'}
    {'field2': 2, 'field1': 'value1'}
 1
Author: Stefan Gruenwald,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-27 20:15:01

Soporta Python2 & 3 usando hook (desde https://stackoverflow.com/a/33571117/558397 )

import requests
import six
from six import iteritems

requests.packages.urllib3.disable_warnings()  # @UndefinedVariable
r = requests.get("http://echo.jsontest.com/key/value/one/two/three", verify=False)

def _byteify(data):
    # if this is a unicode string, return its string representation
    if isinstance(data, six.string_types):
        return str(data.encode('utf-8').decode())

    # if this is a list of values, return list of byteified values
    if isinstance(data, list):
        return [ _byteify(item) for item in data ]

    # if this is a dictionary, return dictionary of byteified keys and values
    # but only if we haven't already byteified it
    if isinstance(data, dict):
        return {
            _byteify(key): _byteify(value) for key, value in iteritems(data)
        }
    # if it's anything else, return it in its original form
    return data

w = r.json(object_hook=_byteify)
print(w)

Devuelve:

 {'three': '', 'key': 'value', 'one': 'two'}
 1
Author: abarik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-21 20:16:47

Esto es tarde para el juego, pero construí este lanzador recursivo. Funciona para mis necesidades y creo que es relativamente completo. Puede ayudarte.

def _parseJSON(self, obj):
    newobj = {}

    for key, value in obj.iteritems():
        key = str(key)

        if isinstance(value, dict):
            newobj[key] = self._parseJSON(value)
        elif isinstance(value, list):
            if key not in newobj:
                newobj[key] = []
                for i in value:
                    newobj[key].append(self._parseJSON(i))
        elif isinstance(value, unicode):
            val = str(value)
            if val.isdigit():
                val = int(val)
            else:
                try:
                    val = float(val)
                except ValueError:
                    val = str(val)
            newobj[key] = val

    return newobj

Simplemente pásale un objeto JSON así:

obj = json.loads(content, parse_float=float, parse_int=int)
obj = _parseJSON(obj)

Lo tengo como un miembro privado de una clase, pero puedes reutilizar el método como mejor te parezca.

 0
Author: Wells,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-10-29 03:53:43

Reescribí la función _parse_json() de Wells para manejar casos donde el objeto json en sí es un array (mi caso de uso).

def _parseJSON(self, obj):
    if isinstance(obj, dict):
        newobj = {}
        for key, value in obj.iteritems():
            key = str(key)
            newobj[key] = self._parseJSON(value)
    elif isinstance(obj, list):
        newobj = []
        for value in obj:
            newobj.append(self._parseJSON(value))
    elif isinstance(obj, unicode):
        newobj = str(obj)
    else:
        newobj = obj
    return newobj
 0
Author: darnmarshall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-07 05:12:22

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.carga.

python speed.py                                                                                            
  json loads            [0.16sec]: {u'a': [{u'b': [[1, 2, [u'\xd6ster..
  json loads + encoding [0.18sec]: {'a': [{'b': [[1, 2, ['\xc3\x96ster.
  time overhead in percent: 9%

Usando esta estructura de prueba:

import json, nested_encode, time

s = """
{
  "firstName": "Jos\\u0301",
  "lastName": "Smith",
  "isAlive": true,
  "age": 25,
  "address": {
    "streetAddress": "21 2nd Street",
    "city": "\\u00d6sterreich",
    "state": "NY",
    "postalCode": "10021-3100"
  },
  "phoneNumbers": [
    {
      "type": "home",
      "number": "212 555-1234"
    },
    {
      "type": "office",
      "number": "646 555-4567"
    }
  ],
  "children": [],
  "spouse": null,
  "a": [{"b": [[1, 2, ["\\u00d6sterreich"]]]}]
}
"""


t1 = time.time()
for i in xrange(10000):
    u = json.loads(s)
dt_json = time.time() - t1

t1 = time.time()
for i in xrange(10000):
    b = nested_encode.encode_nested(json.loads(s))
dt_json_enc = time.time() - t1

print "json loads            [%.2fsec]: %s..." % (dt_json, str(u)[:20])
print "json loads + encoding [%.2fsec]: %s..." % (dt_json_enc, str(b)[:20])

print "time overhead in percent: %i%%"  % (100 * (dt_json_enc - dt_json)/dt_json)
 0
Author: Red Pill,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-04 12:44:46

Tenía un diccionario JSON como cadena. Las claves y valores eran objetos unicode como en el siguiente ejemplo:

myStringDict = "{u'key':u'value'}"

Podría usar la función byteify sugerida anteriormente convirtiendo la cadena en un objeto dict usando ast.literal_eval(myStringDict).

 0
Author: narko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-17 15:17:21

Mira esta respuesta a una pregunta similar como esta que establece que

El prefijo u solo significa que tienes una cadena Unicode. Cuando realmente utiliza la cadena, no aparecerá en sus datos. No te dejes tirar por la salida impresa.

Por ejemplo, prueba esto:

print mail_accounts[0]["i"]

No verás una u.

 0
Author: kunal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-04 13:32:45

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, todavía obtengo las cadenas unicode. Encontré una solución simple usando json.volcado().

response_message = json.loads(json.dumps(response.text))
print(response_message)
 0
Author: Yuelin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-25 17:17:55

También me encontré con este problema, y al tener que lidiar con JSON, se me ocurrió un pequeño bucle que convierte las teclas unicode en cadenas. (simplejson on GAE no devuelve claves de cadena.)

obj es el objeto decodificado desde JSON:

if NAME_CLASS_MAP.has_key(cls):
    kwargs = {}
    for i in obj.keys():
        kwargs[str(i)] = obj[i]
    o = NAME_CLASS_MAP[cls](**kwargs)
    o.save()

kwargs es lo que le paso al constructor de la aplicación GAE (a la que no le gustan las claves unicode en **kwargs)

No tan robusta como la solución de los pozos, pero mucho más pequeña.

 -1
Author: boatcoder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-08-29 17:01:18

He adaptado el código de la respuesta de Mark Amery, particularmente con el fin de deshacerse de isinstance para los pros de la tipificación de pato.

La codificación se realiza manualmente y ensure_ascii está desactivada. Los documentos de python para json.dump dice que

Si ensure_ascii es True (el valor predeterminado), todos los caracteres no ASCII de la salida se escapan con secuencias \uXXXX

Descargo de responsabilidad: en el doctest usé el idioma húngaro. Algunos notables Las codificaciones de caracteres relacionadas con Hungría son: cp852 la codificación IBM/OEM utilizada, por ejemplo. en DOS (a veces referido como ascii, incorrectamente creo, que depende de la configuración codepage), cp1250 utilizado por ejemplo. en Windows (a veces referido como ansi, dependiendo de la configuración regional), y iso-8859-2, a veces utilizado en servidores http. El texto de prueba Tüskéshátú kígyóbűvölő se atribuye a Koltai László (forma de nombre personal nativo) y es de wikipedia .

# coding: utf-8
"""
This file should be encoded correctly with utf-8.
"""
import json

def encode_items(input, encoding='utf-8'):
    u"""original from: https://stackoverflow.com/a/13101776/611007
    adapted by SO/u/611007 (20150623)
    >>> 
    >>> ## run this with `python -m doctest <this file>.py` from command line
    >>> 
    >>> txt = u"Tüskéshátú kígyóbűvölő"
    >>> txt2 = u"T\\u00fcsk\\u00e9sh\\u00e1t\\u00fa k\\u00edgy\\u00f3b\\u0171v\\u00f6l\\u0151"
    >>> txt3 = u"uúuutifu"
    >>> txt4 = b'u\\xfauutifu'
    >>> # txt4 shouldn't be 'u\\xc3\\xbauutifu', string content needs double backslash for doctest:
    >>> assert u'\\u0102' not in b'u\\xfauutifu'.decode('cp1250')
    >>> txt4u = txt4.decode('cp1250')
    >>> assert txt4u == u'u\\xfauutifu', repr(txt4u)
    >>> txt5 = b"u\\xc3\\xbauutifu"
    >>> txt5u = txt5.decode('utf-8')
    >>> txt6 = u"u\\u251c\\u2551uutifu"
    >>> there_and_back_again = lambda t: encode_items(t, encoding='utf-8').decode('utf-8')
    >>> assert txt == there_and_back_again(txt)
    >>> assert txt == there_and_back_again(txt2)
    >>> assert txt3 == there_and_back_again(txt3)
    >>> assert txt3.encode('cp852') == there_and_back_again(txt4u).encode('cp852')
    >>> assert txt3 == txt4u,(txt3,txt4u)
    >>> assert txt3 == there_and_back_again(txt5)
    >>> assert txt3 == there_and_back_again(txt5u)
    >>> assert txt3 == there_and_back_again(txt4u)
    >>> assert txt3.encode('cp1250') == encode_items(txt4, encoding='utf-8')
    >>> assert txt3.encode('utf-8') == encode_items(txt5, encoding='utf-8')
    >>> assert txt2.encode('utf-8') == encode_items(txt, encoding='utf-8')
    >>> assert {'a':txt2.encode('utf-8')} == encode_items({'a':txt}, encoding='utf-8')
    >>> assert [txt2.encode('utf-8')] == encode_items([txt], encoding='utf-8')
    >>> assert [[txt2.encode('utf-8')]] == encode_items([[txt]], encoding='utf-8')
    >>> assert [{'a':txt2.encode('utf-8')}] == encode_items([{'a':txt}], encoding='utf-8')
    >>> assert {'b':{'a':txt2.encode('utf-8')}} == encode_items({'b':{'a':txt}}, encoding='utf-8')
    """
    try:
        input.iteritems
        return {encode_items(k): encode_items(v) for (k,v) in input.iteritems()}
    except AttributeError:
        if isinstance(input, unicode):
            return input.encode(encoding)
        elif isinstance(input, str):
            return input
        try:
            iter(input)
            return [encode_items(e) for e in input]
        except TypeError:
            return input

def alt_dumps(obj, **kwargs):
    """
    >>> alt_dumps({'a': u"T\\u00fcsk\\u00e9sh\\u00e1t\\u00fa k\\u00edgy\\u00f3b\\u0171v\\u00f6l\\u0151"})
    '{"a": "T\\xc3\\xbcsk\\xc3\\xa9sh\\xc3\\xa1t\\xc3\\xba k\\xc3\\xadgy\\xc3\\xb3b\\xc5\\xb1v\\xc3\\xb6l\\xc5\\x91"}'
    """
    if 'ensure_ascii' in kwargs:
        del kwargs['ensure_ascii']
    return json.dumps(encode_items(obj), ensure_ascii=False, **kwargs)

Yo también destacar la respuesta de Jarret Hardie que hace referencia a la JSON spec, cito:

Una cadena es una colección de cero o más caracteres Unicode

En mi caso de uso tenía archivos con json. Son archivos codificados utf-8. ensure_ascii resulta en archivos json correctamente escapados pero no muy legibles, por eso he adaptado la respuesta de Mark Amery para que se ajuste a mis necesidades.

El doctest no es particularmente reflexivo, pero comparto el código con la esperanza de que sea útil para alguien.

 -1
Author: n611x007,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-03 07:59:46