Json-модуль Python, преобразует ключи словаря int в строки - PullRequest
108 голосов
/ 20 сентября 2009

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

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

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

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

Ответы [ 8 ]

72 голосов
/ 20 сентября 2009

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

В 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 ... а затем использовать их для доступа к значениям из сопоставления).

50 голосов
/ 20 сентября 2009

Нет, в JavaScript нет такого понятия, как числовой ключ. Все свойства объекта конвертируются в строку.

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 всегда явно записывает ключи в виде строк, даже если это не выглядит необходимым.

20 голосов
/ 18 сентября 2010

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

<code>
>>>> import json
>>>> json.dumps(releases.items())
    '[[1, "foo-v0.1"]]'
>>>> releases = {1: "foo-v0.1"}
>>>> releases == dict(json.loads(json.dumps(releases.items())))
     True
Я полагаю, что для этого потребуется дополнительная работа, например, наличие какого-то флага, чтобы определить, какие все параметры нужно преобразовать в словарь после его декодирования обратно из json.
12 голосов
/ 18 декабря 2015

Ответ на ваш вопрос:

Это может быть достигнуто с помощью 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

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

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

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 в словаре

9 голосов
/ 03 октября 2009

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

2 голосов
/ 10 февраля 2018

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

import ast
ast.literal_eval(string)
0 голосов
/ 09 января 2019

Вот мое решение! Я использовал 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.

0 голосов
/ 28 июля 2016

Вы можете написать свой json.dumps самостоятельно, вот пример из djson : encoder.py . Вы можете использовать это так:

assert dumps({1: "abc"}) == '{1: "abc"}'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...