使用json.dump在JSON对象之间添加逗号分隔符

12
我正在尝试输出一个包含目录中文件一些属性的json文件。我的问题是,当向文件追加内容时,并没有在每个对象之间添加分隔符。我可以在每个“f”后面添加逗号并删除最后一个,但那似乎不太优雅。
import os
import os.path
import json

#Create and open file_data.txt and append 
with open('file_data.txt', 'a') as outfile:

    files = os.listdir(os.curdir)


    for f in files:

        extension = os.path.splitext(f)[1][1:]
        base = os.path.splitext(f)[0]
        name = f

        data = {
            "file_name" : name,
            "extension" : extension,
            "base_name" : base
                }

        json.dump(data, outfile)

这将输出:

{"file_name": "contributors.txt", "base_name": "contributors", "extension": "txt"}{"file_name": "read_files.py", "base_name": "read_files", "extension": "py"}{"file_name": "file_data.txt", "base_name": "file_data", "extension": "txt"}{"file_name": ".git", "base_name": ".git", "extension": ""}

我想要的是真正的JSON:

{"file_name": "contributors.txt", "base_name": "contributors", "extension": "txt"},{"file_name": "read_files.py", "base_name": "read_files", "extension": "py"},{"file_name": "file_data.txt", "base_name": "file_data", "extension": "txt"},{"file_name": ".git", "base_name": ".git", "extension": ""}

2个回答

19

你得到的不是一个JSON对象,而是一串分离的JSON对象流。

你想要的仍然不是一个JSON对象,而是一串在它们之间带有逗号的分离的JSON对象流。这并不能更容易解析。

* JSON规范足够简单,可以手动解析,而且很明显,在两个对象之间带有逗号的对象不符合任何有效的规则。


如果您想创建一个JSON数组,您可以这样做。显而易见的方法(除非存在内存问题)是构建一个字典列表,然后一次性转储所有内容:
output = []
for f in files:
    # ...
    output.append(data)
json.dump(output, outfile)

如果内存是一个问题,您有几个选择:
  • 为了快速粗暴的解决方案,您可以手动编写[,]来伪造它。(但请注意,即使一些解码器接受它,JSON中有多余的尾随逗号也是无效的。)
  • 您可以将循环封装在生成器函数中,该函数产生每个 data,并扩展 JSONEncoder 将迭代器转换为数组。(请注意,这实际上是文档中用于说明为什么以及如何扩展JSONEncoder示例,尽管您可能需要编写更节省内存的实现。)
  • 您可以查找第三方 JSON 库,该库具有某种内置的迭代流式 API。

然而,值得考虑的是你想要做什么。也许一系列单独的JSON对象实际上是你正在尝试做的文件格式/协议/API。由于JSON是自我分隔的,因此没有必要在不同值之间添加分隔符。(除非你使用不会出现在实际JSON中的分隔符,如",")。例如,你所拥有的正是JSON-RPC应该看起来像的东西。如果你只是因为不知道如何解析这样一个文件而要求不同的东西,那就很容易。例如(为了简单起见,使用字符串而不是文件):
i = 0
d = json.JSONDecoder()
while True:
    try:
        obj, i = d.raw_decode(s, i)
    except ValueError:
        return
    yield obj

3
我有同样的问题,因为我需要将对象yield到文件中,因为我不想将整个对象列表加载到内存中。这是我的方法(但我认为它有点hacky):
json_begin = '{"objects":['
json_end = ']}'

with open('file_data.txt', 'a') as outfile:
   files = os.listdir(os.curdir)

   outfile.write(json_begin)

   for f in files:

       extension = os.path.splitext(f)[1][1:]
       base = os.path.splitext(f)[0]
       name = f

       data = {
           "file_name" : name,
           "extension" : extension,
           "base_name" : base
       }

       json.dump(data, outfile)
       if f != files[-1]:
           outfile.write(',')

   outfile.write(json_end)

有点巧妙,但正是我需要导入到SQL Server的! - Nico Butler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接