Python的'json'模块将int字典键转换为字符串

186 投票
10 回答
97911 浏览
提问于 2025-04-15 14:27

我发现,当运行以下代码时,Python的 json 模块(从2.6版本开始就有)会把字典中的整数键转换成字符串。

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

输出结果:

'{"1": "foo-v0.1"}'

有没有简单的方法可以保持键是整数,而不需要在存储和加载时解析字符串呢?

我觉得可以利用 json 模块提供的钩子来实现,但这仍然需要解析。有没有可能是我忽略了某个参数呢?

附加问题: 谢谢大家的回答。既然 json 的工作方式如我所担心的那样,有没有简单的方法可以通过解析输出结果来传达键的类型呢?

另外,我还要说明,负责存储和从服务器下载JSON对象并加载的代码,都是我自己写的。

10 个回答

34

回答你的子问题:

可以通过使用 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

这个方法会检查值的类型,只有当它们是字符串对象时才会进行转换(准确来说是Unicode字符串)。

这两个函数都假设字典的键(和值)是整数。

感谢以下内容:

我如何在字典推导式中使用if/else?

如何将字典中的字符串键转换为整数

61

不,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总是明确把键写成字符串,即使看起来没有必要。

120

这是不同映射集合之间一些微妙差异的一个例子,可能会让你感到困惑。JSON把键当作字符串来处理,而Python则支持不同类型的键。

在Python(显然在Lua中也是如此),映射(字典或表)的键是对象引用。在Python中,键必须是不可变类型,或者是实现了__hash__方法的对象。(Lua的文档建议,即使是可变对象,它也会自动使用对象的ID作为哈希/键,并依赖字符串的内部存储来确保相同的字符串映射到同一个对象)。

在Perl、JavaScript、awk以及许多其他语言中,哈希、关联数组或这些语言所称的其他名称的键都是字符串(在Perl中称为“标量”)。在Perl中,$foo{1}, $foo{1.0}, 和 $foo{"1"} 都是指向同一个%foo映射的引用——键会被当作标量来评估

JSON最初是作为一种JavaScript序列化技术出现的。(JSON代表JavaScript Object Notation。)因此,它在映射表示法上实现的语义与其映射语义是一致的。

如果你序列化的两端都是Python,那么使用pickle会更好。如果你真的需要将这些从JSON转换回原生的Python对象,我想你有几个选择。首先,你可以尝试使用try: ... except: ...来将任何键转换为数字,以防字典查找失败。或者,如果你在另一端(序列化器或这个JSON数据的生成器)添加代码,你可以让它对每个键值进行JSON序列化——将这些作为一个键的列表提供。(然后你的Python代码会首先遍历这个键的列表,将它们实例化/反序列化为原生的Python对象……然后用这些对象来访问映射中的值)。

撰写回答