298 votes

Comment obtenir des objets chaîne à la place des Unicode de JSON en Python?

Je suis à l'aide de Python pour parser du JSON à partir d' (ASCII codé) des fichiers texte. Lors du chargement de ces fichiers soit avec json ou simplejson, tous mes de la chaîne de valeurs sont exprimées en Unicode objets au lieu de les objets string.

Le problème est, je dois utiliser les données avec certaines bibliothèques qui n'acceptent que des objets string. Je ne peux pas changer les bibliothèques, ni de les mettre à jour.

Est-il possible d'obtenir des objets de chaîne au lieu unicode partir d' json ou simplejson?

Voici un petit exemple:

>>> import json
>>> original_list = ['a', 'b']
>>> json_list = json.dumps(original_list)
>>> json_list
'["a", "b"]'
>>> new_list = json.loads(js)
>>> new_list
[u'a', u'b'] # I want these to be of type `str`, not `unicode`

187voto

Brutus Points 1028

Bien qu'il y ait quelques bonnes réponses ici, j'ai fini par utiliser PyYAML pour analyser les fichiers, cela me donne des objets string. Puisque JSON est un sous-ensemble de YAML, il fonctionne bien dans tous les cas.

 >>> 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.load(list_dump)
['a', 'b']
 

146voto

Mark Amery Points 4705

Il n'y a pas intégré en option pour rendre le module json fonctions retournent des chaînes d'octets au lieu des chaînes unicode. Cependant, cette courte et simple récursif de la fonction de convertir n'importe quel décodé objet JSON d'utiliser les chaînes unicode codé en UTF-8 chaînes d'octets:

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

Appelez simplement présent sur la sortie que vous obtenez à partir d'un json.load ou json.loads appel.

Une dernière remarque: à l'appui de Python 2.6 ou version antérieure, remplacez - return {convert(key): convert(value) for key, value in input.iteritems()} avec return dict([(convert(key), convert(value)) for key, value in input.iteritems()]), depuis le dictionnaire des compréhensions n'étaient pas pris en charge jusqu'à Python 2.7.

76voto

Mike Brennan Points 1384

Vous pouvez utiliser le object_hook paramètre pour json.charges à passer dans un convertisseur. Vous n'avez pas à faire la conversion après le fait. Le json module sera toujours passer la object_hook dicts seulement, et elle sera de manière récursive passer dans les dicts, de sorte que vous n'avez pas à répéter dans imbriquée dicts vous-même. Je ne pense pas que je voudrais convertir des chaînes unicode pour les nombres comme des Puits montre. Si c'est une chaîne unicode, il a été cité comme une chaîne de caractères dans le fichier JSON, donc c'est censé être une chaîne de caractères (ou le fichier est mauvais).

Aussi, je voudrais essayer d'éviter de faire quelque chose comme str(val) sur un objet unicode. Vous devez utiliser la valeur.encode(encodage) avec un encodage valide, selon ce que votre externe lib attend.

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)

39voto

nosklo Points 75862

C'est parce que json a pas de différence entre les objets de chaîne unicode et les objets. Elles sont toutes les chaînes de caractères en javascript.

Je pense que JSON est en droit de retour des objets unicode. En fait, je ne serais pas accepter rien de moins, depuis les chaînes de caractères javascript sont en fait unicode objets (c'est à dire JSON (javascript) les chaînes de caractères peuvent stocker tout type de caractères unicode) il est donc logique de créer unicode objets lors de la traduction des chaînes de JSON. Plaine des chaînes de caractères ne serait pas en forme, puisque la bibliothèque devait deviner l'encodage que vous voulez.

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

Mais si vous voulez vraiment bytestrings, juste encoder les résultats de 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']

9voto

Jarret Hardie Points 36266

J'ai peur, il n'y a aucune façon de le réaliser automatiquement dans la simplejson de la bibliothèque.

Le scanner et le décodeur dans simplejson sont conçus pour produire de 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 de C n'est pas disponible. L' scanstring fonction est appelée plusieurs fois par près de chaque routine qui simplejson a pour le décodage d'une structure qui pourrait contenir du texte. Vous devez soit monkeypatch l' scanstring de la valeur en simplejson.décodeur, ou sous - JSONDecoder et de fournir assez bien votre propre ensemble de la mise en œuvre de tout ce qui peut contenir du texte.

La raison que simplejson sorties unicode, cependant, est que la spécification json mentionne spécifiquement que "Une chaîne est un ensemble de zéro ou plusieurs caractères Unicode"... pour l'unicode est assumé en tant que partie du format lui-même. Simplejson de l' scanstring mise en œuvre va jusqu'à analyser et à interpréter unicode échappe (même vérification des erreurs pour la malformation de multi-octets charset représentations), de sorte que la seule façon fiable de retourner la valeur pour vous, c'est comme de l'unicode.

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X