【问题标题】:Pretty print JSON dumps漂亮的打印 JSON 转储
【发布时间】:2014-03-18 23:22:35
【问题描述】:

我使用此代码将dict 漂亮地打印到 JSON 中:

import json
d = {'a': 'blah', 'b': 'foo', 'c': [1,2,3]}
print json.dumps(d, indent = 2, separators=(',', ': '))

输出:

{
  "a": "blah",
  "c": [
    1,
    2,
    3
  ],
  "b": "foo"
}

这有点太多了(每个列表元素都换行!)。

我应该使用哪种语法来做到这一点:

{
  "a": "blah",
  "c": [1, 2, 3],
  "b": "foo"
}

改为?

【问题讨论】:

  • 这并不完全是您问题的答案,但如果您只是在寻找一种易于使用的数据格式,您可以尝试 YAML。列表和字典等集合有两种语法,“块”(缩进显示嵌套)和“流”(括号完成这项工作)。默认情况下,PyYAML 的输出对不包含任何其他容器的列表和字典使用“流”语法,这听起来正是您想要的漂亮打印。
  • 谢谢@Blckknght。 YAML 是否成熟/稳定/便携并且可能在未来几年/未来版本的 python 中可用?会成为标准吗? (抱歉所有这些问题;))
  • 另一句话:我想避免转换成字符串,因为当我load 将我的 JSON 文件返回到 dict 时,如果它是一个字符串,我将无法再访问list (或者我需要将字符串解析为列表,但这样做很可惜......)
  • 必须通过子类化json.JSONEncoder 来做到这一点,但我还没弄明白。
  • @Basj:我不是真正的 YAML 专家,但我的理解是它相当稳定和成熟,尽管不如 JSON 广泛使用。您可以在the official YAML website 找到它的标准,尽管Wikipedia page 似乎在提供概述方面要好一些。 JSON 是最新版本 YAML 的子集(显然很少遇到与早期 YAML 版本不兼容的问题)。

标签: python json dictionary python-2.x dump


【解决方案1】:

编写自己的 JSON 序列化器:

import numpy

INDENT = 3
SPACE = " "
NEWLINE = "\n"

def to_json(o, level=0):
    ret = ""
    if isinstance(o, dict):
        ret += "{" + NEWLINE
        comma = ""
        for k,v in o.iteritems():
            ret += comma
            comma = ",\n"
            ret += SPACE * INDENT * (level+1)
            ret += '"' + str(k) + '":' + SPACE
            ret += to_json(v, level + 1)

        ret += NEWLINE + SPACE * INDENT * level + "}"
    elif isinstance(o, basestring):
        ret += '"' + o + '"'
    elif isinstance(o, list):
        ret += "[" + ",".join([to_json(e, level+1) for e in o]) + "]"
    elif isinstance(o, bool):
        ret += "true" if o else "false"
    elif isinstance(o, int):
        ret += str(o)
    elif isinstance(o, float):
        ret += '%.7g' % o
    elif isinstance(o, numpy.ndarray) and numpy.issubdtype(o.dtype, numpy.integer):
        ret += "[" + ','.join(map(str, o.flatten().tolist())) + "]"
    elif isinstance(o, numpy.ndarray) and numpy.issubdtype(o.dtype, numpy.inexact):
        ret += "[" + ','.join(map(lambda x: '%.7g' % x, o.flatten().tolist())) + "]"
    else:
        raise TypeError("Unknown type '%s' for json serialization" % str(type(o)))
    return ret

inputJson = {'a': 'blah', 'b': 'foo', 'c': [1,2,3]}
print to_json(inputJson)

输出:

{
   "a": "blah",
   "c": [1,2,3],
   "b": "foo"
}

【讨论】:

  • 真的有那么复杂吗?哇
  • 我期待大约 10 行。我猜Haskell把我宠坏了!
  • @Basj,我知道的只有两种方法:像上面那样编写自己的序列化程序或子类并自定义 JSON 编码器本身(如 docs.python.org/2/library/json.html 所述),我相信这会比前者更痛苦!
  • 您的序列化程序似乎工作得很好,尽管它可能也应该将Nones 转换为'null's。
  • @someDoge,我“认为” pprint 只允许您自定义缩进、宽度和深度,而不是诸如何时吐出新行等内容;因此自定义序列化程序。
【解决方案2】:

也许效率不高,但考虑一个更简单的情况(在 Python 3 中进行了一些测试,但可能也适用于 Python 2):

def dictJSONdumps( obj, levels, indentlevels = 0 ):
    import json
    if isinstance( obj, dict ):
        res = []
        for ix in sorted( obj, key=lambda x: str( x )):
            temp = ' ' * indentlevels + json.dumps( ix, ensure_ascii=False ) + ': '
            if levels:
                temp += dictJSONdumps( obj[ ix ], levels-1, indentlevels+1 )
            else:
                temp += json.dumps( obj[ ix ], ensure_ascii=False )
            res.append( temp )
        return '{\n' + ',\n'.join( res ) + '\n}'
    else:
        return json.dumps( obj, ensure_ascii=False )

这可能会给您一些想法,而不是完全编写自己的序列化程序。我使用了我自己最喜欢的缩进技术,并硬编码了 ensure_ascii,但你可以添加参数并传递它们,或者硬编码你自己的,等等。

【讨论】:

    【解决方案3】:

    另一种选择是print(json.dumps(d, indent=None, separators=(',\n', ': ')))

    输出将是:

    {"a": "blah",
    "c": [1,
    2,
    3],
    "b": "foo"}
    

    请注意,尽管https://docs.python.org/2.7/library/json.html#basic-usage 的官方文档说默认参数是separators=None——这实际上意味着“使用separators=(', ',': ') 的默认值)。另请注意,逗号分隔符不区分 k/v 对并列出元素。

    【讨论】:

    • 谢谢,但这并不是我们所需要的;我们希望将[1, 2, 3] 放在一行中
    • 是的,它并不完美,但与其他怪物相比,它又瘦又卑鄙。 ;) 如果我想变得花哨,我会使用pypi.python.org/pypi/jq
    • Tnx 用于分隔符,帮助我实现 lua 兼容性。在 lua 中不允许有空格。
    【解决方案4】:

    这也困扰了我一段时间,我找到了一个我几乎满意的 1 班轮:

    print json.dumps(eval(str(d).replace('[', '"[').replace(']', ']"').replace('(', '"(').replace(')', ')"')), indent=2).replace('\"\\"[', '[').replace(']\\"\"', ']').replace('\"\\"(', '(').replace(')\\"\"', ')')
    

    本质上将所有列表或元组转换为字符串,然后使用带有缩进的 json.dumps 来格式化字典。然后你只需要删除引号就可以了!

    注意:我将字典转换为字符串以轻松转换所有列表/元组,无论字典的嵌套程度如何。

    附言。我希望 Python Police 不会因为我使用 eval 而来找我……(小心使用)

    【讨论】:

      【解决方案5】:

      我最终使用了jsbeautifier

      import jsbeautifier
      opts = jsbeautifier.default_options()
      opts.indent_size = 2
      jsbeautifier.beautify(json.dumps(d), opts)
      

      输出:

      {
        "a": "blah",
        "c": [1, 2, 3],
        "b": "foo"
      }
      

      【讨论】:

        【解决方案6】:

        多年后,我找到了内置pprint模块的解决方案:

        import pprint
        d = {'a': 'blah', 'b': 'foo', 'c': [1,2,3]}
        pprint.pprint(d)                    # default width=80 so this will be printed in a single line
        pprint.pprint(d, width=20)          # here it will be wrapped exactly as expected
        

        输出:

        {'a': 'blah',  
         'b': 'foo',  
         'c': [1, 2, 3]}
        
        

        【讨论】:

        • 这不一定是有效的 JSON,这是一个印刷精美的 Python 字典,恰好通常是有效的 JSON。
        • @Anonymous 你说得对,但请注意我的问题是关于漂亮打印,而不是关于成为有效的 JSON 字符串。
        【解决方案7】:

        我无法让 jsbeautifier 做很多事情,所以我使用了正则表达式。有类似的json模式

        '{\n    "string": [\n        4,\n        1.0,\n        6,\n        1.0,\n        8,\n        1.0,\n        9,\n        1.0\n    ],\n...'
        

        我想要的

        '{\n    "string": [ 4, 1.0, 6, 1.0, 8, 1.0, 9, 1.0],\n'
        

        所以

        t = json.dumps(apriori, indent=4)
        t = re.sub('\[\n {7}', '[', t)
        t = re.sub('(?<!\]),\n {7}', ',', t)
        t = re.sub('\n {4}\]', ']', t)
        outfile.write(t)
        

        所以我有这 5 行,而不是一个“dump(apriori, t, indent=4)”。

        【讨论】:

        • 欢迎@ChrisChang 加入 StackOverflow!这似乎更像是一个新问题,而不是这个实际问题的答案。可以发new question吗?
        • 嗨@Basj,谢谢。这是对根本问题(Pretty Print JSON Dumps)的回应,但我参考了 Allen Z 关于 jsbeautifier 的帖子。也许我应该把回复和他的回答放在一起,但是关于 jsbeautifier 的评论是切题的。
        猜你喜欢
        • 2014-05-19
        • 2021-01-31
        • 2014-05-12
        • 2017-11-03
        • 2011-05-05
        • 2017-05-01
        • 1970-01-01
        相关资源
        最近更新 更多