web-dev-qa-db-fra.com

Module jyth Python, convertit les clés du dictionnaire int en chaînes

J'ai constaté que, lorsque le programme suivant est exécuté, le module json de python (inclus depuis la version 2.6) convertit les clés de dictionnaire int en chaînes.

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

Existe-t-il un moyen simple de conserver la clé en tant qu'int, sans avoir à analyser la chaîne sur dump and load . Je pense qu'il serait possible d'utiliser les hooks fournis par le module json, mais là encore, il faut analyser Existe-t-il un argument que j'ai négligé? Cheers, chaz

Sous-question: Merci pour les réponses. Étant donné que json fonctionne comme je le craignais, existe-t-il un moyen simple de transmettre le type de clé en analysant peut-être le résultat des dumps? , sont tous deux écrits par moi.

98
Charles Ritchie

C’est l’une de ces différences subtiles parmi les différentes collections de cartes qui peut 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, ils doivent être des types immuables ou des objets qui implémentent une méthode __hash__. (Les documents Lua suggèrent qu'il utilise automatiquement l'ID de l'objet comme clé de hachage/clé même pour les objets mutables et s'appuie sur l'internalisation de chaînes pour s'assurer que des chaînes équivalentes sont mappées vers les mêmes objets).

En Perl, Javascript, awk et dans de nombreux autres langages, les clés pour les hachages, les tableaux associatifs ou ce qu'ils appellent le langage donné sont des chaînes (ou des "scalaires" en Perl). En Perl $foo{1}, $foo{1.0}, and $foo{"1"} sont toutes les références au même mappage dans %foo --- la clé est évaluée en tant que scalaire!

JSON a commencé comme une technologie de sérialisation Javascript. (JSON signifie [J] ava [S] cript [o] bject [n] otation.) Naturellement, il implémente la sémantique pour sa notation de mappage qui est cohérente avec sa sémantique de mappage.

Si les deux extrémités de votre sérialisation doivent être Python, vous feriez mieux d'utiliser des cornichons. Si vous avez vraiment besoin de reconvertir ces fichiers JSON en objets Python natifs, je suppose que vous avez plusieurs choix. Vous pouvez d’abord essayer (try: ... except: ...) de convertir n’importe quelle clé en un nombre en cas d’échec de la consultation du dictionnaire. Si vous ajoutez du code à l’autre extrémité (le sérialiseur ou le générateur de ces données JSON), vous pouvez également l’obliger à effectuer une sérialisation JSON sur chacune des valeurs de clé - en fournissant celles-ci sous forme de liste de clés. (Ensuite, votre code Python devrait d'abord parcourir la liste des clés, les instancier/les désérialiser dans des objets Python natifs ... puis les utiliser pour accéder aux valeurs hors du mappage).

69
Jim Dennis

Non, la clé numérique n'existe pas en JavaScript. Toutes les propriétés de l'objet sont converties 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 appropriés, comme vous le comprendriez dans des langages tels que Python, et l'utilisation de clés qui ne sont pas des chaînes donne un résultat étrange. C'est pourquoi JSON écrit toujours explicitement les clés sous forme de chaînes, même si cela ne semble pas nécessaire.

49
bobince

Vous pouvez également essayer de convertir dictionnaire en une liste de formats [(k1, v1), (k2, v2)] tout en l'encodant à l'aide de json, puis de le reconvertir en dictionnaire après l'avoir décodé.


>>>> 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 nécessitera un peu plus de travail, comme avoir une sorte d'indicateur pour identifier tous les paramètres à convertir en dictionnaire après les avoir décodés à partir de json.

19
Ashish

Répondre à votre sous-question:

Cela 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 fonctionnera également pour les dict imbriqués et utilisera une compréhension de dict.

Si vous souhaitez également convertir les valeurs, 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

Ce qui teste l'instance des valeurs et ne les lance que s'il s'agit d'objets de type chaîne (unicode pour être exact). 

Les deux fonctions supposent que les clés (et les valeurs) sont des entiers.

Grâce à:

Comment utiliser if/else dans une compréhension du dictionnaire?

Convertir une clé de chaîne en int dans un dictionnaire

11
Murmel

J'ai été piqué par le même problème. Comme d'autres l'ont souligné, en JSON, les clés de mappage doivent être des chaînes. Vous pouvez faire l'une des deux choses. Vous pouvez utiliser une bibliothèque JSON moins stricte, telle que demjson , qui autorise les chaînes entières. Si aucun autre programme (ou aucun autre dans d'autres langues) ne le lira, tout devrait bien se passer. Ou vous pouvez utiliser un langage de sérialisation différent. Je ne suggérerais pas cornichon. Il est difficile à lire et n'est pas conçu pour être sécurisé . Au lieu de cela, je suggérerais YAML, qui est (presque) un sur-ensemble de JSON, et autorise les clés entières. (Au moins PyYAML le fait.)

8
AFoglia

Convertissez le dictionnaire en chaîne en utilisant str(dict), puis reconvertissez-le en dict en procédant comme suit:

import ast
ast.literal_eval(string)
2
KhunRan

Voici ma solution! J'ai utilisé object_hook, il est utile lorsque vous avez imbriqué json

>>> import json
>>> json_data = '{"1": "one", "2": {"-3": "minus three", "4": "four"}}'
>>> py_dict = json.loads(json_data, object_hook=lambda d: {int(k) if k.lstrip('-').isdigit() else k: v for k, v in d.items()})

>>> py_dict
{1: 'one', 2: {-3: 'minus three', 4: 'four'}}

Il n'y a filtre que pour l'analyse de json key en int. Vous pouvez également utiliser le filtre int(v) if v.lstrip('-').isdigit() else v pour la valeur json.

0
GooDeeJaY