如何保持用Python 3 json.dumps修复JSON键顺序?
我注意到了Python 3的json.dumps
实现中的一些奇怪行为,即每次将相同的对象从执行转储到执行时,键的顺序都会改变。谷歌搜索没有工作,因为我不关心排序键,我只是希望他们保持不变!下面是一个示例脚本:如何保持用Python 3 json.dumps修复JSON键顺序?
import json
data = {
'number': 42,
'name': 'John Doe',
'email': '[email protected]',
'balance': 235.03,
'isadmin': False,
'groceries': [
'apples',
'bananas',
'pears',
],
'nested': {
'complex': True,
'value': 2153.23412
}
}
print(json.dumps(data, indent=2))
当我运行此脚本,每次都遇到不同的输出,例如:
$ python print_data.py
{
"groceries": [
"apples",
"bananas",
"pears"
],
"isadmin": false,
"nested": {
"value": 2153.23412,
"complex": true
},
"email": "[email protected]",
"number": 42,
"name": "John Doe",
"balance": 235.03
}
但后来我再次运行它,我得到:
$ python print_data.py
{
"email": "[email protected]",
"balance": 235.03,
"name": "John Doe",
"nested": {
"value": 2153.23412,
"complex": true
},
"isadmin": false,
"groceries": [
"apples",
"bananas",
"pears"
],
"number": 42
}
我知道字典是无序的集合,而且订单是基于散列函数的;但是在Python 2中 - 订单(不管它是什么)是固定的,并且不会在每次执行时更改。这里的难点在于它让我的测试难以运行,因为我需要比较两个不同模块的JSON输出!
任何想法是怎么回事?如何解决它?请注意,我想避免使用OrderedDict或执行任何排序,重要的是字符串表示在执行之间保持不变。此外,这仅用于测试目的,对我的模块的实施没有任何影响。
Python字典和JSON对象是无序。您可以可以要求json.dumps()
排序输出中的键;这是为了简化测试。使用sort_keys
参数True
:
print(json.dumps(data, indent=2, sort_keys=True))
见Why is the order in Python dictionaries and sets arbitrary?,为什么你看到一个不同的顺序各一次。
您可以将PYTHONHASHSEED
environment variable设置为一个整数值来“锁定”字典顺序;使用它仅用于运行测试而不是用于生产,因为散列随机化的整点是为了防止攻击者轻易地对程序进行DOS操作。
从你链接的文章中,这就是我所寻找的:“请注意,从Python 3.3开始,随机散列种子也被使用,使得散列冲突不可预测以防止某些类型的拒绝服务(攻击者通过引发大规模散列冲突而导致Python服务器无法响应),这意味着给定字典的顺序也取决于当前Python调用的随机散列种子。 – bbengfort
你知道如何解决哈希种子的测试目的?我的测试要求我不要将额外的参数传递给json.dumps函数。 – bbengfort
@bbengfort:你可以设置['PYTHONHASHSEED'环境变量](https://docs.python.org/3/using/cmdline.html#envvar-PYTHONHASHSEED)为一个整数值。 –
此行为背后的故事是this漏洞。为了防止它,一台PC上的相同散列码在另一台PC上应该不同。
由于兼容性原因,Python 2可能已经禁用了此行为(哈希随机化),因为这会例如破坏doctests。 Python 3可能(假设)不需要可兼容性。
我可以保证订单在Python 2上修复的唯一原因是偶然的,除非'sort_keys = True' –
@WayneWerner它不是偶然的;散列函数是确定性的 - 请参阅下面的注释,在Python 3.3之后的顺序变化,因为包含一个随机散列种子。 – bbengfort
好吧,我站好了!很有意思。 –