Python的'json'模块将int字典键转换为字符串
我发现,当运行以下代码时,Python的 json 模块(从2.6版本开始就有)会把字典中的整数键转换成字符串。
import json
releases = {1: "foo-v0.1"}
json.dumps(releases)
输出结果:
'{"1": "foo-v0.1"}'
有没有简单的方法可以保持键是整数,而不需要在存储和加载时解析字符串呢?
我觉得可以利用 json 模块提供的钩子来实现,但这仍然需要解析。有没有可能是我忽略了某个参数呢?
附加问题: 谢谢大家的回答。既然 json 的工作方式如我所担心的那样,有没有简单的方法可以通过解析输出结果来传达键的类型呢?
另外,我还要说明,负责存储和从服务器下载JSON对象并加载的代码,都是我自己写的。
10 个回答
回答你的子问题:
可以通过使用 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字符串)。
这两个函数都假设字典的键(和值)是整数。
感谢以下内容:
不,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总是明确把键写成字符串,即使看起来没有必要。
这是不同映射集合之间一些微妙差异的一个例子,可能会让你感到困惑。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对象……然后用这些对象来访问映射中的值)。