【问题标题】:Formatting JSON in Python在 Python 中格式化 JSON
【发布时间】:2018-11-06 15:54:15
【问题描述】:

当初始 JSON 字符串的格式没有额外的空格或换行符时,将 JSON 字符串漂亮地打印为带有缩进的字符串的最简单方法是什么?

目前我正在运行json.loads(),然后在结果上运行json.dumps()indent=2。这行得通,但感觉就像我在浪费大量计算资源。

是否有更简单或更高效(内置)的方式来漂亮地打印 JSON 字符串? (同时保持它为有效的 JSON)

示例

import requests
import json

response = requests.get('http://spam.eggs/breakfast')
one_line_json = response.content.decode('utf-8')
pretty_json = json.dumps(json.loads(response.content), indent=2)

print(f'Original: {one_line_json}')
print(f'Pretty: {pretty_json}')

输出:

Original: {"breakfast": ["spam", "spam", "eggs"]}
Pretty: {
  "breakfast": [
    "spam", 
    "spam", 
    "eggs"
    ]
}

【问题讨论】:

  • 你可能会使用漂亮的打印
  • 您必须以一种或另一种方式解析字符串,然后以一种或另一种方式对其进行格式化和打印。我不认为json.loadsjson.dumps 比任何其他方法都贵,例如使用pprint 而不是dumps。可能更快。
  • @Idlehands 然后 OP 在 Python 对象上调用 json.dumps,将其转换为 JSON。整个问题是关于 JSON 输出的。 Python 对象的字符串表示形式与 JSON 不同,尽管它们都很相似。
  • @WhiteHotLoveTiger - 您能否针对您尝试解码的页面类型发布一个可测试的示例?链接“spam.eggs/breakfast”无法解析。如果您提供可用的 URL,我可以更新我的答案以反映解码 HTTP 请求的成本。

标签: python json python-3.x


【解决方案1】:

json.dumps(obj, indent=2) 优于 pprint,因为:

  1. 使用相同的加载方法会更快。
  2. 具有相同或相似的简单性。
  3. 输出将产生有效的 JSON,而pprint 不会。

pprint_vs_dumps.py

import cProfile
import json
import pprint
from urllib.request import urlopen


def custom_pretty_print():
    url_to_read = "https://www.cbcmusic.ca/Component/Playlog/GetPlaylog?stationId=96&date=2018-11-05"
    with urlopen(url_to_read) as resp:
        pretty_json = json.dumps(json.load(resp), indent=2)
    print(f'Pretty: {pretty_json}')


def pprint_json():
    url_to_read = "https://www.cbcmusic.ca/Component/Playlog/GetPlaylog?stationId=96&date=2018-11-05"
    with urlopen(url_to_read) as resp:
        info = json.load(resp)
    pprint.pprint(info)


cProfile.run('custom_pretty_print()')
>>> 71027 function calls (42309 primitive calls) in 0.084 seconds

cProfile.run('pprint_json()')
>>>164241 function calls (140121 primitive calls) in 0.208 seconds

感谢@tobias_k 在此过程中指出我的错误。

【讨论】:

  • 但这比使用json.dumps 快吗?我严重怀疑这一点。此外,结果可能不是有效的 JSON。
  • 啊,好问题,他确实指定了更“高效”,不是吗。我可能错误地将其解释为更少的代码行。我必须分析才能直接比较效率。
  • 代码行数甚至更少,因为您只需将一个函数调用替换为另一个。如果有的话,您不需要indent 参数,但正如我所说:TrueFalseNone 等的无效 JSON。
  • 感谢@tobias_k。我明白你所说的解码成本是什么意思。并且 cProfile 不是与 OP 示例的一对一比较。不幸的是,'spam.eggs/breakfast' 无法解析为可用的示例。
  • 抱歉,您在这里比较的是苹果和橙子。如果您进行时序分析,请正确执行。为什么要从您正在计时的方法的 URL 部分加载 JSON?这将花费大部分时间,并且与问题无关。此外,您正在使用两种不同的方法来加载内容,从而进一步扭曲结果。 另外,在您的第一个示例中,您只是漂亮地打印原始 JSON 字符串,不是 JSON 字典。然后,使用pprint 只打印完整 JSON 对象的 part。如果你只加载一次 JSON,那么json.dumps 会更快一点。
【解决方案2】:

我认为对于真正的 JSON 对象打印,它可能已经达到了最好的水平。 timeit(number=10000) 为以下取约5.659214497s

import json
d = {   
        'breakfast': [
            'spam', 'spam', 'eggs', 
            {
                'another': 'level', 
                'nested': [
                    {'a':'b'}, 
                    {'c':'d'}
                ]
            }
        ], 
        'foo': True,
        'bar': None
    }
s = json.dumps(d)
q = json.dumps(json.loads(s), indent=2)
print(q)

我尝试使用pprint,但它实际上不会打印纯 JSON 字符串,除非它被转换为 Python dict,这会丢失您的 truenullfalse 等有效 JSON,如上所述在另一个答案中。而且它不保留项目出现的顺序,所以如果顺序对可读性很重要,那就不好了。

为了好玩,我创建了以下函数:

def pretty_json_for_savages(j, indentor='  '):
    ind_lvl = 0
    temp = ''
    for i, c in enumerate(j):
        if c in '{[':
            print(indentor*ind_lvl + temp.strip() + c)
            ind_lvl += 1
            temp = ''
        elif c in '}]':
            print(indentor*ind_lvl + temp.strip() + '\n' + indentor*(ind_lvl-1) + c, end='')
            ind_lvl -= 1
            temp = ''
        elif c in ',':
            print(indentor*(0 if j[i-1] in '{}[]' else ind_lvl) + temp.strip() + c)
            temp = ''
        else:
            temp += c
    print('')

# {
#   "breakfast":[
#     "spam",
#     "spam",
#     "eggs",
#     {
#       "another": "level",
#       "nested":[
#         {
#           "a": "b"
#         },
#         {
#           "c": "d"
#         }        
#       ]      
#     }    
#   ],
#   "foo": true,
#   "bar": null
# }

它的打印效果很好,不出所料,16.701202023s 运行在timeit(number=10000) 中,这是json.dumps(json.loads()) 得到的3 倍。除非您花一些时间来优化它,否则可能不值得构建自己的功能来实现这一点,并且由于缺少 builtin 相同的功能,最好还是坚持使用您的枪,因为您的努力很可能会减少返回。

【讨论】:

    猜你喜欢
    • 2021-07-03
    • 2013-01-16
    • 1970-01-01
    • 2022-11-23
    • 2017-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-21
    相关资源
    最近更新 更多