我发现当运行以下命令时,python的json模块(从2.6开始包含)将int字典键转换为字符串。
>>> import json
>>> releases = {1: "foo-v0.1"}
>>> json.dumps(releases)
'{"1": "foo-v0.1"}'
有没有简单的方法将密钥保存为int,而无需在转储和加载时解析字符串。 我相信可以使用json模块提供的钩子,但是这仍然需要解析。 可能有一个我忽略的争论吗? 干杯,chaz
子问题: 谢谢你的回答。看到json像我担心的那样工作,是否有一种通过解析转储输出来传达密钥类型的简单方法? 另外我应该注意执行转储的代码和从服务器下载json对象并加载它的代码都是由我编写的。
答案 0 :(得分:72)
这是可以咬你的各种地图集之间的微妙差异之一。
在Python中(显然在Lua中)映射的键(分别是字典或表)是对象引用。在Python中,它们必须是不可变类型,或者它们必须是实现__hash__
方法的对象。 (Lua文档建议它自动使用对象的ID作为哈希/键,即使是可变对象,并依赖于字符串实习来确保等效字符串映射到相同的对象)。
在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对象......然后使用它们来访问映射中的值。)
答案 1 :(得分:50)
不,JavaScript中没有数字键。所有对象属性都转换为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这样的语言中理解它,并且使用非String的键会导致奇怪。这就是为什么JSON总是将键显式地写为字符串,即使它看起来不必要。
答案 2 :(得分:20)
或者你也可以尝试将字典转换为[(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
我相信这需要更多的工作,比如有一些标志来确定从json解码后要转换为字典的所有参数。
答案 3 :(得分:12)
回答你的问题:
可以使用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
其中测试值的实例并仅在它们是字符串对象时才转换它们(确切地说是unicode)。
两个函数都假定键(和值)是整数。
感谢:
答案 4 :(得分:9)
我被同样的问题困扰了。正如其他人所指出的那样,在JSON中,映射键必须是字符串。你可以做两件事之一。您可以使用不太严格的JSON库,例如demjson,它允许整数字符串。如果没有其他程序(或其他语言中没有其他程序)会读它,那么你应该没问题。或者您可以使用不同的序列化语言。我不会建议泡菜。这很难读,而且是not designed to be secure。相反,我建议YAML,它(几乎)是JSON的超集,并且允许整数键。 (至少PyYAML会这样做。)
答案 5 :(得分:2)
使用str(dict)
将字典转换为字符串,然后执行此操作将其转换回dict:
import ast
ast.literal_eval(string)
答案 6 :(得分:1)
我对Murmel的答案做了一个非常简单的扩展,我认为它可以在相当随意的字典(包括嵌套字典)上工作,前提是它可以首先由JSON转储。任何可以解释为整数的键都将转换为int。毫无疑问,这不是很有效,但是它可以实现我存储到json字符串和从json字符串加载的目的。
def convert_keys_to_int(d: dict):
new_dict = {}
for k, v in d.items():
try:
new_key = int(k)
except ValueError:
new_key = k
if type(v) == dict:
v = _convert_keys_to_int(v)
new_dict[new_key] = v
return new_dict
假设原始字典中的所有键都是整数(如果可以将它们强制转换为int),则在将其存储为json后将返回原始字典。 例如
>>>d = {1: 3, 2: 'a', 3: {1: 'a', 2: 10}, 4: {'a': 2, 'b': 10}}
>>>convert_keys_to_int(json.loads(json.dumps(d))) == d
True
答案 7 :(得分:0)
这是我的解决方案!我使用了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 :(得分:-1)
您可以自己编写json.dumps
,这是djson:encoder.py的示例。您可以像这样使用它:
assert dumps({1: "abc"}) == '{1: "abc"}'