Comment obtenir des objets string au lieu D'Unicode à partir de JSON?
j'utilise Python 2 pour analyser JSON à partir de ASCII encoded fichiers texte.
lors du chargement de ces fichiers avec json
ou simplejson
, toutes les valeurs de mes chaînes de caractères sont attribuées à des objets Unicode plutôt qu'à des objets string. Le problème est que je dois utiliser les données avec des bibliothèques qui n'acceptent que les objets string. I ne peut pas changer les bibliothèques ni les mettre à jour.
est-il possible d'obtenir des objets string à la place des 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`
mise à Jour
cette question a été posée il y a longtemps , quand j'étais coincé avec Python 2 . Une solution facile et propre pour aujourd'hui est d'utiliser une version récente de Python - i.e. Python 3 et en avant.
21 réponses
Une solution 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'usage:
>>> 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 cela fonctionne et pourquoi devrais-je utiliser?
la fonction de Mark Amery est plus courte et plus claire que celles-ci, alors à quoi bon? Pourquoi voudriez-vous utiliser?
Purement performance . La réponse de Mark décode complètement le texte de JSON d'abord avec les chaînes unicode, puis revient à travers toute la valeur décodée pour convertir toutes les chaînes en byte strings. Cela a quelques effets indésirables:
- une copie de toute la structure décodée est créée en mémoire
- si votre objet JSON est vraiment profondément imbriqué (500 niveaux ou plus) alors vous atteindrez la profondeur maximale de récursion de 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 optionnelle qui sera appelée avec le résultat de tout objet littéral décodé (adict
). La valeur de retour de object_hook sera utilisée à la place dudict
. Cette fonctionnalité peut être utilisée pour mettre en œuvre des décodeurs personnalisés
depuis dictionnaires imbriqués à de nombreux niveaux dans d'autres dictionnaires passer à object_hook
comme ils sont décodés , nous pouvons byteify toutes les chaînes ou listes à l'intérieur d'eux à ce point et éviter la nécessité de la récursion profonde plus tard.
la réponse de Mark ne peut pas être utilisée comme object_hook
telle qu'elle est, parce qu'elle revient dans des dictionnaires imbriqués. Nous empêchons que la récursion dans cette réponse avec le paramètre ignore_dicts
à _byteify
, qui obtient passé à lui en tout temps sauf quand object_hook
lui passe un nouveau dict
à byteify. Le drapeau ignore_dicts
indique _byteify
d'ignorer dict
puisqu'ils ont déjà été byteifiés.
enfin, nos implémentations de json_load_byteified
et json_loads_byteified
appel _byteify
(avec ignore_dicts=True
) sur le résultat retourné de json.load
ou json.loads
pour gérer le cas où le texte JSON étant décodé n'a pas dict
au niveau supérieur.
bien qu'il y ait quelques bonnes réponses ici, j'ai fini par utiliser PyYAML pour analyser mes fichiers JSON, car il donne les clés et les valeurs comme str
Type chaînes au lieu de unicode
type. Parce que JSON est un sous-ensemble de YAML il 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']
Notes
certaines choses à noter cependant:
-
je reçois objets string parce que tous les mes entrées sont ASCII codé . Si j'utilisais des entrées codées unicode, je les récupérerais en objets unicode - il n'y a pas de conversion!
-
vous devriez (probablement toujours) utiliser la fonction
safe_load
de PyYAML; si vous l'utilisez pour charger des fichiers JSON, vous n'avez pas besoin de la "puissance supplémentaire" de la fonctionload
de toute façon. -
si vous voulez un YAML parser qui a plus de soutien pour la version 1.2 de la spec (et correctement parses très bas nombres ) try Ruamel YAML :
pip install ruamel.yaml
etimport ruamel.yaml as yaml
é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), utilisez plutôt une conversion fonction :
j'ai utilisé celle de Marque Amery une couple de fois maintenant, il fonctionne très bien et est très facile à utiliser. Vous pouvez également utiliser une fonction similaire comme un object_hook
à la place, car il pourrait vous gagner un boost de performance sur les gros fichiers. Voir la réponse un peu plus impliquée de Mirec Miskuf pour cela.
il n'y a pas d'option intégrée pour que les fonctions du module json renvoient des chaînes d'octets au lieu des chaînes unicode. Cependant, cette fonction recursive simple et courte convertira n'importe quel objet JSON décodé en utilisant des chaînes unicode en chaînes Byte encodées 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
il suffit de l'appeler sur la sortie que vous obtenez à partir d'un appel json.load
ou json.loads
.
quelques notes:
- pour supporter Python 2.6 ou plus tôt, remplacer
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 interprétations de dictionnaires n'étaient pas supportées avant Python 2.7. - puisque cette réponse revient à travers tout l'objet décodé, elle présente quelques caractéristiques de performance indésirables qui peuvent être évitées en utilisant très soigneusement les paramètres
object_hook
ouobject_pairs_hook
. la réponse de Mirec Miskuf est jusqu'à présent la seule qui réussit à tirer cela correctement, bien que comme un la conséquence, c'est beaucoup plus compliqué que mon approche.
vous pouvez utiliser le paramètre object_hook
pour json.loads
pour passer dans un convertisseur. Vous n'avez pas à faire la conversion après le fait. Le module json
passera toujours les dicts object_hook
seulement, et il passera récursivement dans les dicts imbriqués, de sorte que vous n'avez pas à revenir dans les dicts imbriqués vous-même. Je ne pense pas que je convertirais les chaînes unicode en nombres comme Wells montre. Si c'est une chaîne unicode, elle a été citée 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, j'essaierais d'éviter de faire quelque chose comme str(val)
sur un objet unicode
. Vous devez utiliser value.encode(encoding)
avec un encodage valide, en fonction de ce que votre lib externe 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)
c'est parce que json n'a pas de différence entre les objets string et les objets unicode. Ce sont des chaînes en javascript.
je pense JSON est en droit de retour des objets unicode . En fait, je n'accepterais rien de moins, puisque les chaînes javascript sont en fait unicode
objets (c.-à-d. JSON (javascript) chaînes peuvent stocker toute sorte de caractère unicode) il est donc logique de créer unicode
objets lors de la traduction des chaînes de JSON. Les chaînes simples ne conviennent pas puisque la bibliothèque doit deviner l'encodage que vous voulez.
Il est préférable d'utiliser des unicode
chaîne des objets partout. Donc votre meilleure option est de mettre à jour vos bibliothèques pour qu'elles puissent traiter des objets unicode.
mais si vous voulez vraiment bytestrings, il suffit de coder 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.
TL; DR-utiliser ast.literal_eval()
au lieu de json.loads()
. Les deux ast
et json
sont dans la bibliothèque standard.
bien que ce ne soit pas une réponse "parfaite", elle va 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 difficile quand certains objets sont vraiment des chaînes Unicode. Le la réponse complète devient vite velue.
je crains qu'il n'y ait aucun moyen de réaliser cela automatiquement dans la bibliothèque simplejson.
le scanner et le décodeur en 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), ou py_scanstring
si la version C n'est pas disponible. La fonction scanstring
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 surveiller la valeur scanstring
dans simplejson.décodeur ,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 simplejson produit unicode, cependant, est que le JSON spec mentionne spécifiquement que"une chaîne est une collection de zéro ou plus caractères Unicode"... le support d'unicode est supposé faire partie du format lui-même. Simplejson de l' L'implémentation scanstring
va jusqu'à scanner et interpréter les échappées d'unicode (même en vérifiant les erreurs pour les représentations de charset multi-octets mal formées), de sorte que la seule façon fiable de vous retourner la valeur est en unicode.
si vous avez une vieille bibliothèque qui a besoin d'un str
, je vous recommande soit laborieusement rechercher la structure de données imbriquée après analyse (ce que je reconnais est ce que vous avez explicitement dit que vous vouliez éviter... désolé), ou peut-être envelopper votre 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 facile à gérer que la première si vos structures de données sont en effet profondément imbriquées.
la réponse de Mike Brennan est proche, mais il n'y a aucune raison de traverser à nouveau toute la structure. Si vous utilisez object_hook_pairs
(Python 2.7+) paramètre:
object_pairs_hook
est une fonction optionnelle qui sera appelée avec le résultat de n'importe quel objet littéral décodé avec une liste ordonnée de paires. La valeur de retour deobject_pairs_hook
sera utilisé à la place dudict
. Cette fonctionnalité peut être utilisée pour implémenter des décodeurs qui reposent sur l'ordre que les paires clé-valeur sont décodés (par exemple,collections.OrderedDict
se souviendra de l'ordre d'insertion). Siobject_hook
est également défini, leobject_pairs_hook
a priorité.
avec elle, vous obtenez chaque objet JSON remis à vous, de sorte que vous pouvez faire le décodage sans besoin de recursion:
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 n'ai jamais à appeler le crochet récursivement puisque chaque objet sera remis au crochet lorsque vous utilisez le object_pairs_hook
. Vous devez vous soucier des listes, mais comme vous pouvez le voir, un objet à l'intérieur d'une liste sera correctement converti, et vous n'avez pas à recommencer pour le faire se produire.
EDIT: un collaborateur a souligné que Python2.6 n'a pas object_hook_pairs
. Vous pouvez toujours utiliser ce sera Python2.6 en faisant un très petit changement. Dans le crochet ci-dessus, changer:
for key, value in pairs:
à
for key, value in pairs.iteritems():
puis utiliser 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'}}
L'utilisation de object_pairs_hook
résulte en un dictionnaire de moins étant instancié pour chaque objet dans l'objet JSON, qui, si vous parsiez un document énorme, pourrait être utile pendant.
comme marque (Amery) note correctement: L'utilisation de PyYaml 's deserializer sur un dump JSON ne fonctionne que si vous avez ASCII seulement. Au moins hors de la boîte.
deux rapides commentaires sur L'approche PyYaml:
-
jamais utilisez yaml.charge sur les données du champ. Sa fonctionnalité(!) de yaml pour exécuter du code arbitraire caché dans la structure.
-
Vous peut faire fonctionner aussi pour les 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 aucune comparaison à la réponse de Mark Amery:
jetant quelques dicts échantillons profondément emboîtés sur les deux méthodes, j'obtiens ceci (avec dt[j] = delta de temps de JSON.charges(json.dumps (m)):
dt[yaml.safe_load(json.dumps(m))] =~ 100 * dt[j]
dt[byteify recursion(Mark Amery)] =~ 5 * dt[j]
ainsi désérialisation y compris la marche complète de l'arbre et Encodage , bien dans l'ordre de grandeur de l'implémentation basée sur le C. Je trouve cela remarquablement rapide et son aussi plus robuste que la charge yaml à des structures profondément imbriquées. Et moins d'erreurs de sécurité, en regardant yaml.charge.
= > alors que j'apprécierais un pointeur vers un convertisseur C uniquement basé la fonction byteify devrait être la réponse par défaut.
Cela est particulièrement vrai si votre structure json est depuis le champ, contenant les entrées de l'utilisateur. Parce qu'alors vous avez probablement besoin de marcher de toute façon sur votre structure - indépendant sur vos structures de données internes désirées ("sandwich unicode" ou chaînes byte seulement).
pourquoi?
Unicode normalisation "de la 151940920" . Pour les non-avertis: prendre un analgésique et lire ce .
donc en utilisant la recursion de byteify vous tuez deux oiseaux avec une pierre:
- obtenir votre bytestrings de imbriquée json décharges
- normalisez les valeurs d'entrée de l'utilisateur, pour que vous trouviez le contenu de votre stockage.
Dans mes tests, il s'est avéré que le remplacement de l'entrée.encoder ('utf-8') avec une unicodedata.normaliser ('NFC', input).encode ('utf-8') était encore plus rapide que w/o NFC - mais cela dépend fortement des données de l'échantillon je suppose.
le gotcha est que simplejson
et json
sont deux modules différents, au moins dans la façon dont ils traitent unicode. Vous avez json
dans py 2.6+, et cela vous donne des valeurs unicode, alors que simplejson
renvoie des objets string. Il suffit d'essayer easy_install-ing simplejson dans votre environnement et de voir si cela fonctionne. Il l'a fait pour moi.
donc, j'ai rencontré le même problème. Devinez quel a été le premier résultat de Google.
parce que J'ai besoin de 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 de typesafe JSON - json.dump () sauterait sur tous les objets non-littéraux, comme les objets Python. Mais il ne convertit pas les index de 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
il suffit d'utiliser pickle à la place de json pour dump et load, comme ainsi:
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 entiers sont manipulés correctement):
{u'field2': 2, u'field1': u'value1'}
{'field2': 2, 'field1': 'value1'}
supporte Python2 & 3 en utilisant hook (à partir 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)
Retourne:
{'three': '', 'key': 'value', 'one': 'two'}
c'est tard pour le jeu, mais j'ai construit ce jeteur récursif. Il répond à 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
passez-lui un objet JSON comme ceci:
obj = json.loads(content, parse_float=float, parse_int=int)
obj = _parseJSON(obj)
je l'ai comme un membre privé d'une classe, mais vous pouvez réutiliser la méthode que vous voyez l'ajustement.
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).
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
voici un encodeur récursif écrit en C: https://github.com/axiros/nested_encode
surcharge de Performance pour la "moyenne" des structures autour de 10% par rapport à json.charge.
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%
utilisant cette structure d'essai:
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)
j'ai eu un JSON dict comme corde. Les clés et les valeurs étaient des objets unicode comme dans l'exemple suivant:
myStringDict = "{u'key':u'value'}"
je pourrais utiliser la fonction byteify
suggérée ci-dessus en convertissant la chaîne de caractères en un objet dict
en utilisant ast.literal_eval(myStringDict)
.
Check out ce réponse à une question similaire, comme ce qui stipule que
le préfixe u signifie simplement que vous avez une chaîne Unicode. Lorsque vous utilisez la chaîne, il n'apparaîtra pas dans vos données. Ne vous laissez pas jeter par la sortie imprimée.
par exemple, essayez ceci:
print mail_accounts[0]["i"]
vous ne verrez pas un U.
avec Python 3.6, parfois je rencontre encore ce problème. Par exemple, lorsque j'obtiens la réponse d'une API REST et que je charge le texte de réponse à JSON, j'obtiens toujours les chaînes unicode. J'ai trouvé une solution simple en utilisant json.dumps.)(
response_message = json.loads(json.dumps(response.text))
print(response_message)
j'ai rencontré ce problème aussi, et en ayant affaire à JSON, j'ai créé une petite boucle qui convertit les clés unicode en chaînes. ( simplejson
sur GAE ne renvoie pas les clés de chaîne.)
obj
est l'objet décodé de 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 unicode
clés dans **kwargs
)
pas aussi robuste que la solution de Wells, mais beaucoup plus petit.
j'ai adapté le code de la réponse de Mark Amery , en particulier afin de se débarrasser de isinstance
pour les pros de canard-typing.
le codage se fait manuellement et ensure_ascii
est désactivé. Le python docs pour json.dump
dit que
si ensure_ascii est True( par défaut), tous les caractères non-ASCII de la sortie sont echappé avec des séquences \uXXXX
Avertissement: dans le doctest j'ai utilisé la langue hongroise. Parmi les encodages de caractères liés à la Hongrie, on peut citer: cp852
l'encodage IBM/OEM utilisé par ex. dans DOS (parfois dénommé ascii , à tort je pense, il dépend de la codepage ), cp1250
utilisé par exemple. dans Windows (parfois appelé ansi , dépendant de la paramètres régionaux), et iso-8859-2
, parfois utilisé sur les serveurs http. Le texte d'essai Tüskéshátú kígyóbűvölő
est attribué à Koltai László (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éponse de jarret Hardie qui fait référence à la JSON spec , citant:
une chaîne est une collection de zéro ou plus caractères Unicode
dans mon étui j'avais des fichiers avec json. Ce sont des fichiers codés utf-8
. ensure_ascii
se traduit par des fichiers JSON correctement échappés mais pas très lisibles, c'est pourquoi J'ai adapté la réponse de Mark Amery à mes besoins.
La doctest n'est pas particulièrement attentionné, mais je partage le code dans l'espoir qu'il sera utile pour quelqu'un.