J'utilise Python 2 pour analyser JSON à partir de fichiers texte encodés ASCII .
Lors du chargement de ces fichiers avec json
ou simplejson
, toutes mes valeurs de chaîne sont converties en objets Unicode au lieu d'objets chaîne. Le problème est que je dois utiliser les données avec certaines bibliothèques qui n'acceptent que les objets chaîne. Je ne peux pas changer les bibliothèques ni les mettre à jour.
Est-il possible d'obtenir des objets chaîne au lieu d'objets Unicode?
Exemple
>>> 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`
Mettre à jour
Cette question a été posée il y a longtemps , lorsque j'étais coincé avec Python 2 . Une solution simple et claire pour aujourd'hui consiste à utiliser une version récente de Python, c'est-à-dire Python 3 et versions ultérieures .
str
Réponses:
Une solution avec
object_hook
Exemple d'utilisation:
Comment ça marche et pourquoi devrais-je l'utiliser?
La fonction de Mark Amery est plus courte et plus claire que celles-ci, alors quel est leur intérêt? Pourquoi voudriez-vous les utiliser?
Purement pour la performance . La réponse de Mark décode le texte JSON entièrement en premier avec des chaînes unicode, puis revient dans toute la valeur décodée pour convertir toutes les chaînes en chaînes d'octets. Cela a quelques effets indésirables:
Cette réponse atténue ces deux problèmes de performances en utilisant le
object_hook
paramètre dejson.load
etjson.loads
. De la documentation :Étant donné que les dictionnaires imbriqués de nombreux niveaux profondément dans d'autres dictionnaires sont passés à
object_hook
fur et mesure qu'ils sont décodés , nous pouvons byteifier toutes les chaînes ou listes à l'intérieur à ce stade et éviter la nécessité d'une récursivité profonde plus tard.La réponse de Mark ne peut pas être utilisée telle
object_hook
quelle, car elle se reproduit dans des dictionnaires imbriqués. Nous empêchons cette récursivité dans cette réponse avec leignore_dicts
paramètre to_byteify
, qui lui est transmis à tout moment sauf lorsqu'il luiobject_hook
passe un nouveaudict
à byteify. leignore_dicts
drapeau indique_byteify
d'ignorer lesdict
s car ils ont déjà été byteifiés.Enfin, nos implémentations de
json_load_byteified
etjson_loads_byteified
appelons_byteify
(avecignore_dicts=True
) le résultat renvoyé parjson.load
oujson.loads
pour gérer le cas où le texte JSON en cours de décodage n'a pas dedict
niveau supérieur.la source
return { byteify(key, ignore_dicts=True): _byteify(value, ignore_dicts=True) for key, value in data.iteritems() }
avecreturn dict((_byteify(key, ignore_dicts=True), _byteify(value, ignore_dicts=True)) for key, value in data.iteritems())
pour que cela fonctionne.json_loads_byteified('[' * 990 + ']' * 990)
. Avec 991, il plante. Marc travaille toujours avec 991:byteify(json.loads('[' * 991 + ']' * 991))
. Il plante à 992. Donc, au moins dans ce test, Mark peut aller plus loin, contrairement à ce que vous avez dit.Bien qu'il y ait de bonnes réponses ici, j'ai fini par utiliser PyYAML pour analyser mes fichiers JSON, car il donne les clés et les valeurs sous
str
forme de chaînes deunicode
type au lieu de type. Parce que JSON est un sous-ensemble de YAML, il fonctionne bien:Remarques
Quelques points à noter cependant:
J'obtiens des objets chaîne car toutes mes entrées sont encodées en ASCII . Si j'utilisais des entrées codées unicode, je les récupérerais en tant qu'objets unicode - il n'y a pas de conversion!
Vous devriez (probablement toujours) utiliser la
safe_load
fonction de PyYAML ; si vous l'utilisez pour charger des fichiers JSON, vous n'avez de toute façon pas besoin de la "puissance supplémentaire" de laload
fonction.Si vous voulez un analyseur YAML qui prend davantage en charge la version 1.2 de la spécification (et analyse correctement des nombres très faibles ), essayez Ruamel YAML :
pip install ruamel.yaml
etimport ruamel.yaml as yaml
c'était tout ce dont j'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 que les valeurs ASCII (et vous ne pouvez pas être sûr la plupart du temps), mieux vaut utiliser une fonction de conversion :
J'ai utilisé celui de Mark Amery plusieurs fois maintenant, il fonctionne très bien et est très facile à utiliser. Vous pouvez également utiliser une fonction similaire à la
object_hook
place, car cela pourrait vous permettre d' améliorer les performances des gros fichiers. Voir la réponse un peu plus complexe de Mirec Miskuf pour cela.la source
yaml.load(json.dumps([u'a', u'£', u'É']))
sur le shell Python et observez que vous revenez['a', u'\xa3', u'\xc9']
(qui contient desunicode
chaînes). Si vous ne pouvez pas être sûr que vos données ne contiennent que des caractères du jeu de caractères ASCII, vous devez utiliser une approche différente à la place (je recommande ma propre réponse).[u'a', u'b']
également la prudence.Il n'y a pas d'option intégrée pour que les fonctions du module json retournent des chaînes d'octets au lieu de chaînes unicode. Cependant, cette fonction récursive courte et simple convertira tout objet JSON décodé de l'utilisation de chaînes unicode en chaînes d'octets encodées en UTF-8:
Appelez simplement cela sur la sortie que vous obtenez d'un
json.load
oujson.loads
appelez.Quelques notes:
return {byteify(key): byteify(value) for key, value in input.iteritems()}
parreturn dict([(byteify(key), byteify(value)) for key, value in input.iteritems()])
, car les compréhensions de dictionnaire n'étaient pas prises en charge avant Python 2.7.object_hook
ouobject_pairs_hook
. La réponse de Mirec Miskuf est jusqu'à présent la seule qui réussit à réussir cela, bien qu'en conséquence, c'est beaucoup plus compliqué que mon approche.la source
object_hook
est en réalité bien pire que celle-ci, mais, en utilisantobject_pairs_hook
, vous pouvez trouver une méthode raisonnablement efficace qui ne nécessite aucune récursivité ou réexamen des nœuds qui ne contiennent pas de chaînes.object_pairs_hook
méthode est peut-être très légèrement plus difficile à comprendre que celle-ci (vous devez comprendre comment le paramètre fonctionne et pourquoi les listes et les dict nécessitent une manipulation différente), et l'avantage de la performance n'aura pas d'importance pour la plupart des gens ... mais je m'attendrais à il existe, en particulier pour toute personne traitant un objet JSON anormalement profondément imbriqué.Vous pouvez utiliser le
object_hook
paramètre pourjson.loads
passer dans un convertisseur. Vous n'avez pas à effectuer la conversion après coup. Lejson
module transmettra toujours lesobject_hook
dict uniquement, et il transmettra récursivement les dicts imbriqués, vous n'avez donc pas besoin de récursivement vous-même dans les dicts imbriqués. Je ne pense pas que je convertirais des chaînes unicode en nombres comme le montre Wells. S'il s'agit d'une chaîne unicode, elle a été citée en tant que chaîne dans le fichier JSON, elle est donc censée être une chaîne (ou le fichier est incorrect).De plus, j'essaierais d'éviter de faire quelque chose comme
str(val)
sur ununicode
objet. Vous devez utiliservalue.encode(encoding)
avec un encodage valide, selon ce que votre bibliothèque externe attend.Ainsi, par exemple:
la source
s
est un JSONObject
(une collection non ordonnée de paires clé: valeur avec le caractère ':' séparant la clé et la valeur, séparées par des virgules et entourées d'accolades), mais pas si c'est, disons, un JSONArray
. Donc, si on donne un JSONArray
comme["a", "b"]
, le résultat sera toujours[u'a', u'b']
. Aucun des autres paramètres de type de crochet de personnalisation actuellement disponibles pourjson.loads()
ne peut faire le travail non plus.json
module passera récursivement desdict
s imbriqués , il n'est pas nécessaire de les vérifier dans les deux fonctions - les deuxelif
clauses qui les vérifient doivent donc être supprimées.from Utility import *
, les fonctions ne seront pas visibles à cause de ce trait de soulignement.object_hook
est appelé pour chaque objet json analysé, donc si vous récursivement dans ce qui vous est donné, vous «ré-octetez» les choses que vous avez déjà «byteifiées». Les performances vont croître géométriquement avec la taille de l'objet. J'ai inclus ici une réponse qui utiliseobject_pairs_hook
et ne souffre pas de ce problème.C'est parce que json n'a aucune différence entre les objets chaîne et les objets unicode. Ce sont toutes des chaînes en javascript.
Je pense que JSON a raison de renvoyer des objets Unicode . En fait, je n'accepterais rien de moins, car les chaînes javascript sont en fait des
unicode
objets (c'est-à-dire que les chaînes JSON (javascript) peuvent stocker tout type de caractère unicode), il est donc logique de créer desunicode
objets lors de la traduction de chaînes à partir de JSON. Les chaînes simples ne conviennent tout simplement pas car la bibliothèque devrait deviner l'encodage que vous souhaitez.Il vaut mieux utiliser
unicode
des objets chaîne partout. Votre meilleure option est donc de mettre à jour vos bibliothèques afin qu'elles puissent traiter les objets Unicode.Mais si vous voulez vraiment des bytestrings, encodez simplement les résultats dans l'encodage de votre choix:
la source
Il existe une solution de contournement facile.
TL; DR - Utilisez
ast.literal_eval()
au lieu dejson.loads()
. Les deuxast
etjson
sont dans la bibliothèque standard.Bien que ce ne soit pas une réponse `` parfaite '', elle en obtient une assez loin si votre plan est d'ignorer complètement Unicode. En Python 2.7
donne:
Cela devient plus velu lorsque certains objets sont vraiment des chaînes Unicode. La réponse complète devient rapidement poilue.
la source
null
,true
ou desfalse
valeurs, parce qu'ils ne sont pas valides en Python et causerontliteral_eval()
à l' échec.\/
) à l'intérieur d'une chaîne, ou une séquence d'échappement unicode (comme"\u0061"
, qui est une autre façon d'écrire"a"
). La syntaxe littérale de Python est incompatible avec JSON de plusieurs manières, et je ne ferais pas confiance à cette réponse pour un script que je n'allais pas jeter.json
pour vider les données, utilisez simplementprint
si vous exécutez python. Puisast.literal_eval
travailleLa réponse de Mike Brennan est proche, mais il n'y a aucune raison de traverser à nouveau la structure entière. Si vous utilisez le
object_hook_pairs
paramètre (Python 2.7+):Avec lui, vous obtenez chaque objet JSON, vous pouvez donc faire le décodage sans avoir besoin de récursivité:
Notez que je n'ai jamais à appeler le crochet de manière récursive car chaque objet sera remis au crochet lorsque vous utiliserez le
object_pairs_hook
. Vous devez vous soucier des listes, mais comme vous pouvez le voir, un objet dans une liste sera correctement converti, et vous n'avez pas besoin de récursivement pour y arriver.EDIT: Un collègue a souligné que Python2.6 n'a pas
object_hook_pairs
. Vous pouvez toujours utiliser cette volonté Python2.6 en faisant un très petit changement. Dans le crochet ci-dessus, modifiez:à
Utilisez ensuite
object_hook
au lieu deobject_pairs_hook
:L'utilisation de
object_pairs_hook
résultats dans un dictionnaire de moins est instanciée pour chaque objet dans l'objet JSON, ce qui, si vous analysiez un document volumineux, pourrait valoir la peine.la source
deunicodify_hook
que vous présentez dans cette réponse? Pour le moment, vous avez une implémentationdeunicodify_hook
qui n'itère pas sur les listes et ne décode pas les chaînes et les listes qu'elles contiennent, et donc la sortie que vous exposez ne correspond pas à la sortie que votre hook va réellement produire. Corrigez cela, et cette réponse sera supérieure à la mienne.object_pairs_hook
seul objet est appelé pour les objets , si votre texte JSON a une liste de chaînes au niveau supérieur, cette solution échouera. Il n'y a aucun moyen de résoudre ce problème sans appeler une fonction sur la chose retournéejson.load
; aucun desjson.load
crochets ne peut garantir que vous serez capable de gérer chaque chaîne. Je pense que c'est un assez gros défaut pour que je continue de recommander ma solution plutôt que d'utiliser les crochets.Je crains qu'il n'y ait aucun moyen d'y parvenir automatiquement dans la bibliothèque simplejson.
Le scanner et le décodeur de simplejson sont conçus pour produire du texte unicode. Pour ce faire, la bibliothèque utilise une fonction appelée
c_scanstring
(si elle est disponible, pour la vitesse), oupy_scanstring
si la version C n'est pas disponible. Lascanstring
fonction est appelée plusieurs fois par presque chaque routine que simplejson a pour décoder une structure qui pourrait contenir du texte. Vous devez soit monkeypatch lascanstring
valeur dans simplejson.decoder, ou sousJSONDecoder
- classe et fournir à peu près votre propre implémentation entière de tout ce qui pourrait contenir du texte.La raison pour laquelle simplejson génère unicode, cependant, est que la spécification json mentionne spécifiquement que "Une chaîne est une collection de zéro ou plusieurs caractères Unicode" ... la prise en charge de l'unicode est supposée faire partie du format lui-même. L'
scanstring
implémentation de Simplejson va jusqu'à analyser et interpréter les échappements unicode (même en vérifiant les erreurs pour les représentations de jeux de caractères multi-octets mal formées), donc la seule façon dont il peut vous renvoyer la valeur de manière fiable est en tant qu'unicode.Si vous avez une bibliothèque ancienne qui a besoin d'un
str
, je vous recommande soit de rechercher laborieusement la structure de données imbriquée après l'analyse (ce que je reconnais être ce que vous avez explicitement dit que vous vouliez éviter ... désolé), soit d'envelopper vos bibliothèques dans une sorte de façade où vous pouvez masser les paramètres d'entrée à un niveau plus granulaire. La deuxième approche pourrait être plus gérable que la première si vos structures de données sont en effet profondément imbriquées.la source
Comme Mark (Amery) le note correctement: L' utilisation du désérialiseur de PyYaml sur un vidage json ne fonctionne que si vous avez uniquement ASCII. Au moins hors de la boîte.
Deux commentaires rapides sur l'approche PyYaml:
N'utilisez JAMAIS yaml.load sur les données du champ. C'est une fonctionnalité (!) De yaml pour exécuter du code arbitraire caché dans la structure.
Vous pouvez le faire fonctionner également pour non ASCII via ceci:
Mais la performance n'est pas comparable à la réponse de Mark Amery:
En lançant des exemples de dict profondément imbriqués sur les deux méthodes, j'obtiens ceci (avec dt [j] = delta temporel de json.loads (json.dumps (m))):
Donc, la désérialisation, y compris la marche complète de l'arbre et l' encodage, bien dans l'ordre de grandeur de l'implémentation basée sur C de json. Je trouve cela remarquablement rapide et aussi plus robuste que la charge de yaml sur les structures profondément imbriquées. Et moins sujet aux erreurs de sécurité, en regardant yaml.load.
=> Bien que j'apprécierais un pointeur vers un convertisseur basé uniquement sur C, la fonction byteify devrait être la réponse par défaut.
Cela est particulièrement vrai si votre structure json provient du champ, contenant des entrées utilisateur. Parce qu'alors vous devrez probablement marcher de toute façon sur votre structure - indépendamment de vos structures de données internes souhaitées («sandwich unicode» ou chaînes d'octets uniquement).
Pourquoi?
Normalisation Unicode . Pour les ignorants: prenez un analgésique et lisez ceci .
Donc, en utilisant la récursion byteify, vous tuez deux oiseaux avec une pierre:
Lors de mes tests, il s'est avéré que remplacer le fichier input.encode ('utf-8') par un unicodedata.normalize ('NFC', input) .encode ('utf-8') était encore plus rapide que sans NFC - mais c'est fortement tributaire des données d'exemple, je suppose.
la source
Le gotcha est cela
simplejson
et cejson
sont deux modules différents, au moins dans la manière dont ils traitent avec l'unicode. Vous avezjson
dans py 2.6+, et cela vous donne des valeurs unicode, tandis quesimplejson
renvoie des objets chaîne. Essayez simplement easy_install-ing simplejson dans votre environnement et voyez si cela fonctionne. Ça l'a fait pour moi.la source
Utilisez simplement pickle au lieu de json pour le vidage et le chargement, comme ceci:
La sortie qu'il produit est (les chaînes et les entiers sont traités correctement):
la source
safe_load
dans YAML, je ne sais pas si quelque chose de similaire existe pour les cornichons .J'ai donc rencontré le même problème. Devinez quel a été le premier résultat Google.
Comme je dois transmettre toutes les données à PyGTK, les chaînes unicode ne me sont pas très utiles non plus. J'ai donc une autre méthode de conversion récursive. Il est en fait également nécessaire pour la conversion JSON de typeafe - json.dump () serait mis en place sur tous les non-littéraux, comme les objets Python. Ne convertit cependant pas les index dict.
la source
J'avais un dict JSON comme chaîne. Les clés et les valeurs étaient des objets Unicode comme dans l'exemple suivant:
Je pourrais utiliser la
byteify
fonction suggérée ci-dessus en convertissant la chaîne en undict
objet en utilisantast.literal_eval(myStringDict)
.la source
{u'key':u'value'}
n'est pas JSON.Prise en charge de Python2 et 3 à l'aide du crochet (depuis https://stackoverflow.com/a/33571117/558397 )
Retour:
la source
C'est la fin du jeu, mais j'ai construit ce lanceur récursif. Cela fonctionne pour mes besoins et je pense que c'est relativement complet. Cela peut vous aider.
Passez-lui simplement un objet JSON comme ceci:
Je l'ai en tant que membre privé d'une classe, mais vous pouvez réutiliser la méthode comme bon vous semble.
la source
json.loads
appel est d'abord nécessaire), essaie arbitrairement de convertir des chaînes en entiers sans raison expliquée, et n'est pas copier-et- coller prêt.J'ai réécrit _parse_json () de Wells pour gérer les cas où l'objet json lui-même est un tableau (mon cas d'utilisation).
la source
voici un encodeur récursif écrit en C: https://github.com/axiros/nested_encode
Surcoût de performance pour les structures "moyennes" d'environ 10% par rapport à json.loads.
en utilisant cette structure de test:
la source
Avec Python 3.6, parfois je rencontre toujours ce problème. Par exemple, lorsque j'obtiens une réponse d'une API REST et que je charge le texte de la réponse dans JSON, j'obtiens toujours les chaînes unicode. Trouvé une solution simple en utilisant json.dumps ().
la source
J'ai également rencontré ce problème, et devant faire face à JSON, j'ai trouvé une petite boucle qui convertit les clés unicode en chaînes. (
simplejson
sur GAE ne renvoie pas de clés de chaîne.)obj
est l'objet décodé à partir de JSON:kwargs
est ce que je passe au constructeur de l'application GAE (qui n'aime pas lesunicode
clés**kwargs
)Pas aussi robuste que la solution de Wells, mais beaucoup plus petit.
la source
J'ai adapté le code de la réponse de Mark Amery , notamment pour se débarrasser des
isinstance
pros de la frappe de canard.L'encodage se fait manuellement et
ensure_ascii
est désactivé. La documentation de python pourjson.dump
dit queAvertissement: dans le doctorat, j'ai utilisé la langue hongroise. Certains encodages de caractères liés à la Hongrie notables sont:
cp852
l'encodage IBM / OEM utilisé par exemple. sous DOS (parfois appelé ascii , à tort je pense, cela dépend du réglage de la page de code ),cp1250
utilisé par exemple. sous Windows (parfois appelé ansi , en fonction des paramètres régionaux), etiso-8859-2
parfois utilisé sur les serveurs http. Le texte du testTüskéshátú kígyóbűvölő
est attribué à Koltai László (forme de nom personnel natif) et provient de wikipedia .Je voudrais également souligner la réponse de Jarret Hardie qui fait référence à la spécification JSON , citant:
Dans mon cas d'utilisation, j'avais des fichiers avec json. Ce sont
utf-8
des fichiers encodés.ensure_ascii
se traduit par des fichiers json correctement échappés mais peu lisibles, c'est pourquoi j'ai adapté la réponse de Mark Amery à mes besoins.Le docteur n'est pas particulièrement réfléchi mais je partage le code dans l'espoir qu'il sera utile à quelqu'un.
la source
json.loads
seront des listes ou des dict, pas un type défini par l'utilisateur ou défini par la bibliothèque qui implémente leurs méthodes et méthodes magiques, alors pourquoi ne pas simplement faire uneisinstance
vérification? N'est-ce pas plus facile à comprendre que de vérifier l'existenceiteritems
ouiter
l'acceptation de l'objet comme argument?Découvrez cette réponse à une question similaire comme celle-ci qui dit que
Le préfixe u signifie simplement que vous avez une chaîne Unicode. Lorsque vous utilisez vraiment la chaîne, elle n'apparaîtra pas dans vos données. Ne soyez pas éjecté par la sortie imprimée.
Par exemple, essayez ceci:
Vous ne verrez pas un u.
la source
'{}'.format({u'x' : u'y'})
comprend toujours les u.