在 python 中输出 JSON 内容很奇怪

Weird thing about JSON content output in python

如果已经有人问过,请原谅我。我从一个简单的 python 代码片段中看到了一些奇怪的结果:

import json
''' copy contents of one json file into another '''
with open('./new_file.json', 'w') as f1:
    with open('./old_file.json') as f2:
        data = json.load(f2)            
        json.dump(data, f1)

上面的代码片段将一个 json 文件的内容复制到另一个文件中。原始文件没有漂亮的格式(即内容全部在一行上)

在 运行 代码之后,我执行以下操作:

$ diff -q new_file.json old_file.json
Files new_file.json and old_file.json differ

文件内容明显不同,很奇怪。 然后我尝试做:

$ cat new_file.json | python -m json.tool > foo
$ cat old_file.json | python -m json.tool > bar

然后当我比较文件 bar 和 foo 时,我得到:

:~/$ diff -q foo bar
:~/$

说明格式化后的内容是一样的。有什么解释吗?

提前致谢。

您已经指出:文件格式。一个文件只有一行,另一个文件可能有更多行。空格可以来来去去...所有这些格式化的东西使文件不同,但内容基本相同。

这是您所看到的行为的最小重新表达。仔细阅读它,看看您是否仍然对为什么 new==oldFalsefoo==barTrue 感到困惑。这几乎是不言自明的。

import json
def tool(jsons):
    return json.dumps(json.loads(jsons))

old = '[{"key": "value"     }]'
new = tool(old)
print(f'{old} == {new}', new == old)
# [{"key": "value"     }] == [{"key": "value"}] False

foo = tool(new)
bar = tool(old)
print(f'{foo} == {foo}', foo == bar)
# [{"key": "value"}] == [{"key": "value"}] True

对其进行扩展。您看到的是 运行 该工具修复了所有不需要的格式。所以最初这两个文件是不同的。然而,由于 re运行 工具在已经格式化的东西上只是 returns 相同的结果,运行 在旧文件和新文件上的工具只会重现相同的输出两次,这是格式化输出。

json 对象是 defined as "an unordered set of name/value pairs",Python 字典也是(至少到 python 3.7),所以 json.load() 创建的字典可以他们的钥匙顺序不同。然后当你 json.dump() 那些字典时,它是用于生成 json 的当前字典键顺序。

示例:

$ cat old.json
{"a": 42, "c": {"baaz": "quux", "foo": "bar"}, "b": [1, 2, 3]}

$ cat new.json
{"a": 42,  "b": [1, 2, 3], "c": {"foo": "bar", "baaz": "quux"}}

$ diff -q old.json new.json
Les fichiers old.json et new.json sont différents

d$ python
(...)
>>> import json
>>> with open("old.json") as f:
...     old = json.load(f)
>>> with open("new.json") as f:
...     new = json.load(f)
... 
>>> old == new
True
>>> 

关键在于,由于 json 中的键顺序并不重要,diff 不是检查两个 json 字符串是否真的不同的正确工具 - 而且它们是确实有很多工具可以做到 proper json diff.

正如 Poshi 和 Vincent 提到的,这两个 json 字符串等同于 json 数据,但与文本不同:

{"a": 42, "c": {"baaz": "quux", "foo": "bar"}, "b": [1, 2, 3]}
{"a":42,     "c"     :  {"baaz":"quux"   , "foo": "bar" }   , "b"  : [  1,   2,   3] }