Python json module, преобразует ключи словаря в строки

Я обнаружил, что, когда выполняется следующее, модуль python json (включенный с версии 2.6) преобразует ключи словаря в строки.

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

Есть ли простой способ сохранить ключ как int, без необходимости разбирать строку в дампе и загрузке. Я считаю, что было бы возможно использовать крючки, предоставляемые модулем json, но опять же это требует разбора. Возможно, есть аргумент, который я упустил? cheers, chaz

Суб-вопрос: Спасибо за ответы. Видя, как json работает, как я боялся, есть ли простой способ передать ключевой тип, возможно, разобрав вывод свалков? Также я должен отметить, что код, выполняющий сброс, и код, загружающий json-объект с сервера и загружающий его, оба написаны мной.

Ответ 1

Это один из тех тонких различий между различными коллекциями картографических данных, которые могут вас укусить.

В Python (и, по-видимому, в Lua) ключи к сопоставлению (словарь или таблица, соответственно) являются объектными ссылками. В Python они должны быть неизменяемыми типами, или они должны быть объектами, которые реализуют метод __hash__. (Документы Lua предполагают, что он автоматически использует идентификатор объекта как хэш/ключ даже для изменяемых объектов и полагается на интернирование строк, чтобы гарантировать, что эквивалентные строки сопоставляются с одними и теми же объектами).

В Perl, Javascript, awk и многих других языках ключи для хэшей, ассоциативных массивов или того, что они вызывают для данного языка, являются строками (или "скалярами" в Perl). В perl $foo{1}, $foo{1.0}, and $foo{"1"} все ссылки на одно и то же отображение в %foo --- ключ оценивается как скаляр!

JSON запускается как технология сериализации Javascript. (JSON означает [J] ava [S] cript [o] bject [n] otation.) Естественно, он реализует семантику для своих обозначений отображения, которые согласуются с его семантикой отображения.

Если оба конца вашей сериализации будут Python, тогда вам будет лучше использовать соленья. Если вам действительно нужно преобразовать их обратно из JSON в родные объекты Python, я думаю, у вас есть несколько вариантов. Сначала вы можете попробовать (try: ... except: ...) преобразовать любой ключ в число в случае сбоя поиска в словаре. В качестве альтернативы, если вы добавите код на другой конец (сериализатор или генератор данных JSON), вы можете заставить его выполнять сериализацию JSON по каждому из значений ключа, предоставляя их как список ключей. (Тогда ваш код Python сначала перебирает список ключей, создавая/десериализуя их в собственные объекты Python... и затем используйте их для доступа к значениям вне отображения).

Ответ 2

Нет, в JavaScript нет такой вещи, как Number. Все свойства объекта преобразуются в String.

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

Это может привести к некоторому любопытно выглядящему поведению:

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

Объекты JavaScript на самом деле не являются правильными сопоставлениями, так как вы понимаете их на таких языках, как Python, а использование ключей, которые не являются строками, приводит к странности. Вот почему JSON всегда явно пишет ключи как строки, даже если они не выглядят необходимыми.

Ответ 3

В качестве альтернативы вы также можете попробовать преобразовать словарь в список форматов [(k1, v1), (k2, v2)] при кодировании с помощью json и преобразовать его обратно в словарь после его декодирования.


>>>> import json
>>>> json.dumps(releases.items())
    '[[1, "foo-v0.1"]]'
>>>> releases = {1: "foo-v0.1"}
>>>> releases == dict(json.loads(json.dumps(releases.items())))
     True
I believe this will need some more work like having some sort of flag to identify what all parameters to be converted to dictionary after decoding it back from json.

Ответ 4

Отвечая на ваше подзапрос:

Это можно сделать, используя 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

Эта функция также будет работать для вложенных dicts и использует понимание dict.

Если вы хотите также указать значения, используйте:

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

Которая проверяет экземпляр значений и отбрасывает их только в том случае, если они являются объектами строк (точнее, юникодом).

Обе функции предполагают, что ключи (и значения) являются целыми числами.

Благодаря:

Как использовать if/else в понимании словаря?

Преобразование строкового ключа в int в словаре

Ответ 5

Я был укушен той же проблемой. Как указывали другие, в JSON ключи отображения должны быть строками. Вы можете сделать одну из двух вещей. Вы можете использовать менее строгую библиотеку JSON, такую ​​как demjson, которая позволяет целые строки. Если никакие другие программы (или никакие другие на других языках) не прочтут это, тогда вы должны быть в порядке. Или вы можете использовать другой язык сериализации. Я бы не предложил мариновать. Трудно читать и не предназначен для защиты. Вместо этого я предлагаю YAML, который является (почти) надмножеством JSON и позволяет использовать целые ключи. (По крайней мере PyYAML делает.)

Ответ 6

Преобразуйте словарь в строку, используя str(dict) а затем преобразуйте его обратно в dict, выполнив следующее:

import ast
ast.literal_eval(string)

Ответ 7

Вот мое решение! Я использовал object_hook, это полезно, когда вы object_hook 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'}}

Есть фильтр только для разбора ключа json на int. Вы можете использовать int(v) if v.lstrip('-').isdigit() else v фильтровать значение json.

Ответ 8

Вы можете написать свой json.dumps самостоятельно, вот пример из djson: encoder.py. Вы можете использовать его следующим образом:

assert dumps({1: "abc"}) == '{1: "abc"}'