Question

J'utilise Python 2 pour analyser JSON de ASCII encodées fichiers texte.

Lors du chargement de ces fichiers avec soit json ou

Était-ce utile?

La solution

Une solution avec 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

Exemple d'utilisation:

>>> 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'}

Comment ça marche et pourquoi devrais-je utiliser?

fonction de Mark Amery est plus courte et plus claire que ceux-ci, alors quel est le point d'eux? Pourquoi voudriez-vous les utiliser?

Purement performances . La réponse de Mark décode le texte JSON entièrement d'abord avec les chaînes unicode, récursif puis à travers la totalité de la valeur décodée pour convertir toutes les chaînes en chaînes d'octets. Cela a deux effets indésirables:

  • Une copie de la totalité de la structure décodée est créée en mémoire
  • Si votre objet JSON est vraiment profondément imbriquées (500 niveaux ou plus), alors vous aurez atteint la profondeur maximum de récursivité Python

Cette réponse atténue ces deux problèmes de performance en utilisant le paramètre object_hook de json.load et json.loads. De les docs :

  

object_hook est une fonction en option qui sera appelée par la suite d'un littéral d'objet décodé (a dict). La valeur de retour de object_hook sera utilisé à la place du dict. Cette fonction peut être utilisée pour mettre en œuvre des décodeurs personnalisés

Depuis dictionnaires imbriqués plusieurs niveaux de profondeur dans d'autres dictionnaires sont transmis à object_hook car ils sont décodées , nous pouvons byteify toutes les chaînes ou les listes à l'intérieur eux à ce moment-là et d'éviter la nécessité d'une récursion profonde plus tard.

La réponse de Mark ne convient pas pour une utilisation en tant object_hook tel qu'il est, parce qu'il récursif dans les dictionnaires imbriqués. Nous empêchons que récursion dans cette réponse avec le paramètre ignore_dicts à _byteify, qui se transmet à elle en tout temps sauf quand object_hook passe une nouvelle dict à byteify. Le drapeau ignore_dicts d'ignorer _byteify indique à dicts depuis qu'ils ont déjà été byteified.

Enfin, nos implémentations de json_load_byteified et json_loads_byteified appel _byteify (avec ignore_dicts=True) sur le résultat renvoyé de json.load ou json.loads pour traiter le cas où le texte JSON décodé ne dispose pas d'un dict au niveau supérieur.

Autres conseils

Bien qu'il existe quelques bonnes réponses ici, je me suis retrouvé à l'aide de PyYAML pour analyser mes fichiers JSON, car il donne la clés et valeurs sous forme de chaînes de type str au lieu de type unicode. Parce que JSON est un sous-ensemble de YAML cela fonctionne 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']

Remarques

Il y a des choses à noter que:

  • Je reçois objets chaîne parce que toutes mes entrées sont ASCII encodée . Si j'utiliser les entrées codées unicode, je les récupérer comme objets unicode - il n'y a pas de conversion

  • Vous devriez (probablement toujours) utiliser la fonction de safe_load PyYAML; si vous l'utilisez pour charger des fichiers JSON, vous n'avez pas besoin de la « puissance supplémentaire » de la fonction de load de toute façon.

  • Si vous voulez un analyseur YAML qui a un plus grand soutien pour la version 1.2 de la spécification (et correctement parse nombres très bas ) essayer Ruamel YAML : pip install ruamel.yaml et import ruamel.yaml as yaml était tout ce que je avais besoin dans mes tests

  • .

Conversion

Comme indiqué, il n'y a pas de conversion! Si vous ne pouvez pas être sûr de ne traiter avec des valeurs ASCII (et vous ne pouvez pas être sûr que la plupart du temps), mieux utiliser une fonction de conversion :

je une Mark Amery deux fois maintenant, il fonctionne très bien et est très facile à utiliser. Vous pouvez également utiliser une fonction similaire en tant object_hook au lieu, car il pourrait vous gagner un gain de performances sur les grands dossiers. Voir la réponse un peu plus impliqué de Mirec Miskuf pour cela.

Il n'y a pas d'option intégrée pour rendre les fonctions du module JSON renvoient des chaînes d'octets au lieu de chaînes unicode. Cependant, cette fonction récursive court et simple vous permet de convertir tout objet JSON décodé d'utiliser des chaînes unicode à des chaînes d'octets UTF-8 codé:

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

Il suffit d'appeler cela sur la sortie que vous obtenez à partir d'un appel json.load ou json.loads.

Quelques notes:

  • Pour le support de Python 2.6 ou une version antérieure, remplacer return {byteify(key): byteify(value) for key, value in input.iteritems()} avec return dict([(byteify(key), byteify(value)) for key, value in input.iteritems()]), puisque compréhensions dictionnaire ne sont pas pris en charge jusqu'à ce que Python 2.7.
  • Depuis cette réponse récursif à travers l'ensemble de l'objet décodé, il a quelques caractéristiques de performance indésirables qui peuvent être évités avec une utilisation très prudent des paramètres de object_hook ou object_pairs_hook. Mirec de réponse Miskuf est jusqu'à présent le seul qui parvient à retirer ceci correctement, bien que par conséquent, il est beaucoup plus compliqué que mon approche.

Vous pouvez utiliser le paramètre object_hook json.loads pour passer dans un convertisseur. Vous ne devez pas faire la conversion après le fait. json le module aura toujours passer les dicts de object_hook seulement, et il sera récursive passe dans dicts imbriqués, de sorte que vous n » t doivent récursivité dans dicts imbriqués vous-même. Je ne pense pas que je convertir des chaînes unicode à des numéros comme le montre Wells. Si c'est une chaîne unicode, il a été cité comme une chaîne dans le fichier JSON, il est censé être une chaîne (ou le fichier est mauvais).

En outre, je vais essayer d'éviter de faire quelque chose comme str(val) sur un objet unicode. Vous devez utiliser value.encode(encoding) avec un codage valide, selon ce que votre attend lib externe.

Ainsi, par exemple:

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)

C'est parce que JSON a pas de différence entre les objets de chaîne et les objets unicode. Ils sont toutes les chaînes en javascript.

Je pense que JSON est juste de retourner des objets unicode . En fait, je ne l'accepte rien de moins, puisque les chaînes javascript sont en fait des objets unicode (c.-à-JSON (JavaScript) Les chaînes peuvent stocker tout type de caractère unicode) il est logique de créer des objets unicode lors de la traduction des chaînes de JSON. cordes lisses juste ne correspondraient depuis la bibliothèque devrait deviner l'encodage que vous voulez.

Il est préférable d'utiliser des objets string unicode partout. Donc, votre meilleure option est de mettre à jour vos bibliothèques afin qu'ils puissent traiter avec des objets unicode.

Mais si vous voulez vraiment des chaînes ordinaires, simplement encoder les résultats à l'encodage de votre choix:

>>> nl = json.loads(js)
>>> nl
[u'a', u'b']
>>> nl = [s.encode('utf-8') for s in nl]
>>> nl
['a', 'b']

Il existe un travail facile autour.

TL; DR - Utilisation ast.literal_eval() au lieu de json.loads(). Les deux ast et json sont dans la bibliothèque standard.

Sans une réponse « parfaite », il obtient un assez loin si votre plan est d'ignorer Unicode tout à fait. 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))

donne:

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

Cela devient plus poilu lorsque certains objets sont vraiment des chaînes Unicode. La réponse complète devient rapidement poilu.

réponse de Mike Brennan est proche, mais il n'y a aucune raison de re-traverse toute la structure. Si vous utilisez le paramètre object_hook_pairs (Python 2.7+):

  

object_pairs_hook est une fonction en option qui sera appelée par la suite de tout objet décodé littéral avec une liste ordonnée de paires. La valeur de retour de object_pairs_hook sera utilisé à la place du dict. Cette fonction peut être utilisée pour mettre en œuvre des décodeurs personnalisés qui dépendent de l'ordre que les paires de clés et de valeur sont décodés (par exemple, collections.OrderedDict se souviendra de l'ordre d'insertion). Si object_hook est également défini, object_pairs_hook est prioritaire.

Avec elle, vous obtenez chaque objet JSON remis à vous, afin que vous puissiez faire le décodage sans avoir besoin de récursion:

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'}}

Notez que je ne dois appeler le crochet récursive car chaque objet sera remis obtenir au crochet lorsque vous utilisez le object_pairs_hook. Vous ne devez prendre soin sur les listes, mais comme vous pouvez le voir, un objet dans une liste sera correctement converti, et vous ne devez pas récursif pour y arriver.

EDIT: Un collègue de travail a souligné que python2.6 n'a pas object_hook_pairs. Vous pouvez toujours utiliser ce sera python2.6 en faisant un petit changement. Dans le crochet ci-dessus, changer:

for key, value in pairs:

à

for key, value in pairs.iteritems():

Utilisez ensuite object_hook au lieu 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'}}

En utilisant les résultats de object_pairs_hook dans un dictionnaire moins instancié pour chaque objet dans l'objet JSON, qui, si vous l'analyse d'un document énorme, pourrait être utile.

Je crains qu'il n'y ait aucun moyen d'y parvenir automatiquement dans la bibliothèque simplejson.

Le scanner et le décodeur dans simplejson sont conçus pour produire un texte unicode. Pour ce faire, la bibliothèque utilise une fonction appelée c_scanstring (si elle est disponible, pour la vitesse), ou py_scanstring si la version C est pas disponible. La fonction scanstring est appelée à plusieurs reprises par presque toutes les routine qui simplejson a pour décoder une structure qui pourrait contenir du texte. Il faudrait soit la valeur monkey-patch scanstring dans simplejson.decoder, ou sous-classe JSONDecoder et de fournir à peu près votre propre mise en œuvre complète de tout ce qui pourrait contenir du texte.

La raison pour laquelle les sorties simplejson unicode, cependant, est que le JSON spécifications mentionne spécifiquement que « Une chaîne est une collection de zéro ou plusieurs caractères Unicode » ... soutien unicode est acquis dans le cadre du format lui-même. La mise en œuvre de scanstring de simplejson va jusqu'à analyser et interpréter les évasions unicode (même de vérification des erreurs pour les représentations malformées charset multi-octets), la seule façon qu'il puisse revenir de manière fiable la valeur de vous est comme unicode.

Si vous avez une bibliothèque d'âge qui a besoin d'un str, je vous recommande soit de rechercher laborieusement la structure de données imbriquée après l'analyse (que je reconnais est ce que vous avez dit que vous vouliez explicitement éviter ... désolé), ou peut-être enveloppez vos bibliothèques dans une sorte de façade où vous pouvez masser les paramètres d'entrée à un niveau plus granulaire. La seconde approche pourrait être plus facile à gérer que la première si vos structures de données sont en effet profondément imbriquées.

Comme Mark (Amery) note correctement: Utiliser PyYAML de 'la désérialiseur sur une décharge JSON ne fonctionne que si vous avez ASCII seulement. Au moins hors de la boîte.

Deux commentaires rapides sur l'approche PyYAML:

  1. JAMAIS utilisation yaml.load sur les données du terrain. Son une caractéristique (!) De YAML d'exécuter du code arbitraire caché dans la structure.

  2. peut le faire fonctionner aussi pour non ASCII via ceci:

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

Mais la performance sage de son pas de comparaison à la réponse de Mark Amery:

Lancer des échantillons profondément imbriquées dicts sur les deux méthodes, je reçois ce (avec [j] dt = delta temps de json.loads (json.dumps (m))):

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

désérialisation y compris la marche complètement l'arbre et encodage, bien dans l'ordre de grandeur de la mise en œuvre à base de C de JSON. Je trouve cela remarquablement rapide et plus robuste est aussi que la charge de YAML à des structures profondément imbriquées. Et moins d'erreurs de sécurité ventral, regardant yaml.load.

=> Alors que je vous serais reconnaissant un pointeur vers un convertisseur C uniquement en fonction de la byteify fonction doit être la réponse par défaut.

Cela est particulièrement vrai si la structure de votre JSON est du champ, contenant l'entrée d'utilisateur. Parce que vous avez probablement besoin de marcher de toute façon sur votre structure - indépendante sur vos structures de données internes souhaitées ( « sandwich unicode » ou des chaînes d'octets uniquement)

.

Pourquoi?

Unicode normalisation . Pour le pas au courant. Prenez un anti-douleur et lire cette

Donc, en utilisant la récursion byteify vous tuer deux oiseaux avec une pierre:

  1. obtenir vos chaînes d'octets de décharges JSON imbriquées
  2. obtenir l'entrée d'utilisateur des valeurs normalisées, afin que vous trouviez les choses dans votre stockage.

Dans mes tests, il est apparu que le remplacement de la input.encode ( 'utf-8') avec un unicodedata.normalize ( 'NFC', entrée) .encode ( 'utf-8') était encore plus rapide que w / o NFC -. mais c'est fortement dépendante des données d'exemple que je suppose que

Le gotcha est que simplejson et json deux modules différents, au moins dans la manière dont ils traitent unicode. Vous avez json dans py 2.6+, et cela vous donne des valeurs unicode, alors que des objets chaîne renvoie simplejson. Juste essayer easy_install-ing simplejson dans votre environnement et voir si cela fonctionne. Il a fait pour moi.

Il suffit d'utiliser cornichon au lieu de JSON pour décharge et charge, comme suit:

    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 sortie qu'il produit est (les chaînes et les nombres entiers sont traités correctement):

    {u'field2': 2, u'field1': u'value1'}
    {'field2': 2, 'field1': 'value1'}

Alors, j'ai couru dans le même problème. Devinez ce qui a été le premier résultat Google.

Parce que je dois passer toutes les données à PyGTK, les chaînes unicode ne sont pas très utiles pour moi non plus. J'ai donc une autre méthode de conversion récursive. Il est en fait également nécessaire pour la conversion typesafe JSON - json.dump () renflouerait sur les non-littéraux, comme des objets Python. Ne convertit pas les index dict bien.

# 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

J'ai eu un dict JSON comme une chaîne. Les clés et les valeurs sont des objets unicode comme dans l'exemple suivant:

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

je puisse utiliser la fonction de byteify suggéré ci-dessus par la conversion de la chaîne de caractères à un objet en utilisant dict ast.literal_eval(myStringDict).

Support python2 et 3 en utilisant le crochet (de 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)

Renvoie:

 {'three': '', 'key': 'value', 'one': 'two'}

est en retard dans le jeu, mais je construit ce lanceur de sorts récursive. Il travaille pour mes besoins et je pense qu'il est relativement complet. Il peut vous aider.

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

Il suffit de passer un objet JSON comme ceci:

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

Je l'ai en tant que membre privé d'une classe, mais vous pouvez réutiliser la méthode comme bon vous semble.

Je réécris la _parse_json Wells () pour traiter les cas où l'objet JSON lui-même est un tableau (mon cas d'utilisation).

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

ici est un codeur récursif écrit en C: https://github.com/axiros/nested_encode

frais généraux de performance pour les structures "moyennes" environ 10% par rapport à json.loads.

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%

en utilisant ce teststructure:

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)

Vérifiez cette réponse à une question similaire comme celle qui stipule que

Le préfixe u- signifie simplement que vous avez une chaîne Unicode. Lorsque vous utilisez vraiment la chaîne, il ne sera pas apparaître dans vos données. Ne pas être jeté par la sortie imprimée.

Par exemple, essayez ceci:

print mail_accounts[0]["i"]

Vous ne verrez pas u.

Avec Python 3.6, parfois je lance encore ce problème. Par exemple, lors de l'obtention d'une réponse API REST et charger le texte de réponse à JSON, je reçois toujours les chaînes unicode. Trouvé une solution simple à l'aide json.dumps ().

response_message = json.loads(json.dumps(response.text))
print(response_message)

Je suis tombé sur ce problème aussi, et d'avoir à faire face à JSON, je suis venu avec une petite boucle qui convertit les clés unicode à cordes. (simplejson sur GAE ne retourne pas les clés de chaîne.)

obj est l'objet décodé à partir 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 est ce que je passe au constructeur de l'application GAE (qui n'aime pas les clés de unicode dans **kwargs)

pas aussi robuste que la solution de Wells, mais beaucoup plus petit.

Je l'ai adapté le code de la répondre de Mark Amery , en particulier afin de se débarrasser de isinstance pour les pros de canard frappe.

L'encodage se fait manuellement et ensure_ascii est désactivé. Les docs python pour json.dump dit que

  

Si ensure_ascii est vrai (la valeur par défaut), tous les caractères non-ASCII dans la sortie sont échappés avec \ séquences uXXXX

Avertissement: dans la doctest je la langue hongroise. Quelques notables codages de caractères liés hongroise sont: cp852 l'encodage IBM / OEM utilisé par exemple. sous DOS (parfois désigné comme ascii , à tort je pense, il dépend de la codepage réglage), cp1250 utilisés, par exemple. dans Windows (parfois appelé comme ansi , en fonction des paramètres régionaux) et iso-8859-2, parfois utilisé sur les serveurs http. Le texte de test Tüskéshátú kígyóbűvölő est attribué à Koltai László (sous forme de nom personnel natif) et est 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)

Je voudrais aussi souligner la répondre de Jarret Hardie qui fait référence à la JSON spécifications , citant:

  

Une chaîne est un ensemble de zéro ou plusieurs caractères Unicode

Dans mon cas d'utilisation j'avais des fichiers avec JSON. Ils sont des fichiers codés utf-8. les résultats de ensure_ascii correctement échappées mais pas très lisibles fichiers JSON, c'est pourquoi j'ai adapté la réponse de Mark Amery pour répondre à mes besoins.

Le doctest est pas particulièrement réfléchi, mais je partage le code dans l'espoir qu'il sera utile pour quelqu'un.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top