Le module JSON de Python convertit les clés de dictionnaires en chaînes

j'ai trouvé que lorsque ce qui suit est exécuté, le module JSON de python (inclus depuis 2.6) convertit les clés de dictionnaires en chaînes.

>>> import json
>>> releases = {1: "foo-v0.1"}
>>> json.dumps(releases)
'{"1": "foo-v0.1"}'

est là n'importe quel moyen facile de préserver la clé comme un int, sans avoir besoin de parser la corde sur le dump et charger. Je pense qu'il serait possible d'utiliser les crochets fournis par le module json, mais encore une fois cela nécessite encore l'analyse. Est-il éventuellement un argument que j'ai négligé? santé, chaz

Sous-question: Merci pour les réponses. Vu que json fonctionne comme je le craignais, y a-t-il un moyen facile de transmettre le type de clé en analysant peut-être la sortie des dumps? Aussi je devrais noter le code faisant le dumping et le code téléchargeant l'objet json d'un serveur et le chargeant, sont tous les deux écrits par moi.

88
demandé sur Charles Ritchie 2009-09-20 16:52:40

7 réponses

c'est l'une de ces subtiles différences parmi les diverses collections de cartographie qui peuvent vous mordre.

en Python (et apparemment en Lua) les clés d'un mapping (dictionnaire ou table, respectivement) sont des références d'objet. En Python, il doit s'agir de types immuables, ou d'objets qui implémentent une méthode __hash__ . (Les documents Lua suggèrent qu'il utilise automatiquement L'ID de l'objet comme une clé de hachage / même pour les objets mutables et s'appuie sur l'interning string pour s'assurer que les chaînes équivalentes correspondent aux mêmes objets).

en Perl, Javascript, awk et beaucoup d'autres langues les clés pour les hashes, les tableaux associatifs ou tout ce qu'ils sont appelés pour le langage donné, sont des chaînes (ou" scalars " en Perl). En perl $foo{1}, $foo{1.0}, and $foo{"1"} sont toutes les références à la même cartographie dans %foo - - la clé est évalué comme un scalaire!

JSON a commencé comme une technologie de sérialisation Javascript. (JSON est synonyme de [J]ava[S]cript [o]objet [n]introduction en bourse.) Naturellement il met en œuvre la sémantique pour sa notation de cartographie qui sont compatibles avec sa sémantique de cartographie.

Si les deux extrémités de votre sérialisation vont être Python alors vous feriez mieux d'utiliser les cornichons. Si vous avez vraiment besoin de convertir ceux-ci de retour de JSON en objets Python natifs je suppose que vous avez un couple de choix. Tout d'abord, vous pouvez essayer ( try: ... except: ... ) de convertir n'importe quelle clé à un nombre dans le cas d'un dictionnaire recherche de panne. Alternativement, si vous ajoutez du code à l'autre extrémité (le serializer ou le générateur de ces données JSON) alors vous pourriez lui faire effectuer une sérialisation JSON sur chacune des valeurs clés --- fournissant ceux comme une liste de clés. (Puis votre code Python itérera d'abord sur la liste des clés, en les instanciant/les désérialisant en objets Python natifs ... et ensuite utiliser ces pour accéder aux valeurs de la cartographie).

64
répondu Jim Dennis 2014-08-23 00:38:18

Non, il n'y a pas de clé de numéro en JavaScript. Toutes les propriétés des objets sont convertis en Chaîne.

var a= {1: 'a'};
for (k in a)
    alert(typeof k); // 'string'

cela peut conduire à des comportements curieux:

a[999999999999999999999]= 'a'; // this even works on Array
alert(a[1000000000000000000000]); // 'a'
alert(a['999999999999999999999']); // fail
alert(a['1e+21']); // 'a'

les objets JavaScript ne sont pas vraiment des mappages corrects comme vous le comprendriez dans des langages comme Python, et utiliser des clés qui ne sont pas des chaînes de caractères entraîne des résultats étranges. C'est la raison pour laquelle JSON écrit toujours explicitement les clés sous forme de chaînes, même là où elles ne semblent pas nécessaire.

49
répondu bobince 2009-09-20 13:17:22

vous pouvez aussi essayer de convertir le dictionnaire en une liste de [(k1,v1),(k2,v2)] format tout en l'encodant en utilisant json, et en le convertissant de nouveau au dictionnaire après le décodage.


>>>> import json
>>>> json.dumps(releases.items())
    '[[1, "foo-v0.1"]]'
>>>> releases = {1: "foo-v0.1"}
>>>> releases == dict(json.loads(json.dumps(releases.items())))
     True
Je pense que cela va avoir besoin de plus de travail comme avoir une sorte de drapeau pour identifier ce que tous les paramètres à convertir en Dictionnaire après le décodage de retour de json.
17
répondu Ashish 2010-09-18 18:17:49

j'ai été mordu par le même problème. Comme d'autres l'ont souligné, dans JSON, les clés de mapping doivent être des chaînes. Vous pouvez faire une des deux choses. Vous pouvez utiliser une bibliothèque JSON moins stricte, comme demjson , qui permet des chaînes entières. Si aucun autre programme (ou aucun autre dans d'autres langues) va le lire, alors vous devriez être d'accord. Ou vous pouvez utiliser un langage de sérialisation différent. Je ne suggérerais pas cornichon. Il est difficile à lire, et est pas conçu pour être sécurisé . Au lieu de cela, je suggérerais YAML, qui est (presque) un super-ensemble de JSON, et permet des touches entières. (Au moins PyYAML does.)

6
répondu AFoglia 2013-08-14 07:01:09

répondant à votre sous-question:

il peut être accompli en utilisant json.loads(jsonDict, object_hook=jsonKeys2int)

def jsonKeys2int(x):
    if isinstance(x, dict):
            return {int(k):v for k,v in x.items()}
    return x

cette fonction fonctionne également pour les dicts imbriqués et utilise une compréhension dict.

si vous voulez lancer les valeurs aussi, utilisez:

def jsonKV2int(x):
    if isinstance(x, dict):
            return {int(k):(int(v) if isinstance(v, unicode) else v) for k,v in x.items()}
    return x

qui teste l'instance des valeurs et ne les projette que si elles sont des objets strings (unicode pour être exact).

les deux fonctions suppose clés (et les valeurs) pour des entiers.

merci à:

Comment utiliser les if/else dans un dictionnaire de compréhension?

convertissez une clé à chaîne en int dans un dictionnaire

6
répondu Murmel 2018-08-24 15:00:17

convertissez le dictionnaire en chaîne en utilisant str(dict) et convertissez-le en dict en faisant ceci:

import ast
ast.literal_eval(string)
2
répondu KhunRan 2018-02-10 09:43:19

vous pouvez écrire votre json.dumps par vous-même, voici un exemple de djson : encoder.py . Vous pouvez l'utiliser comme ceci:

assert dumps({1: "abc"}) == '{1: "abc"}'
-1
répondu damnever 2016-07-28 02:17:43