如何从文本文件中获取多个未用分隔符分隔的JSON对象?

20 投票
9 回答
23786 浏览
提问于 2025-04-17 09:32

我有成千上万的文本文件,这些文件里包含了多个JSON对象,但不幸的是,这些对象之间没有任何分隔符。

这些对象是以字典的形式存储的,有些字段本身也是对象。每个对象可能会有不同数量的嵌套对象。具体来说,一个对象可能长这样:

{field1: {}, field2: "some value", field3: {}, ...} 

而在一个文本文件中,这样的对象成百上千地连在一起,没有任何分隔符。这就意味着我不能使用 json.load() 或者 json.loads() 来处理它们。

有没有什么建议可以解决这个问题?有没有已知的解析器可以做到这一点?

9 个回答

4

Sebastian Blask的回答提到了一个不错的思路,但对于这么简单的修改,使用正则表达式其实没必要。

objs = json.loads("[%s]"%(open('your_file.name').read().replace('}{', '},{')))

或者,写得更清楚一些

raw_objs_string = open('your_file.name').read() #read in raw data
raw_objs_string = raw_objs_string.replace('}{', '},{') #insert a comma between each object
objs_string = '[%s]'%(raw_objs_string) #wrap in a list, to make valid json
objs = json.loads(objs_string) #parse json
4

解决方案

据我所知,}{ 这个组合在有效的 JSON 中是不会出现的,所以当你想从连接在一起的字符串中提取出单独的对象时,下面的方法是完全安全的(txt 是你文件的内容)。这不需要任何导入(甚至不需要导入 re 模块)来实现:

retrieved_strings = map(lambda x: '{'+x+'}', txt.strip('{}').split('}{'))

或者如果你喜欢用列表推导式(正如 David Zwicker 在评论中提到的),你可以这样使用:

retrieved_strings = ['{'+x+'}' for x in txt.strip('{}').split('}{'))]

这样做的结果是 retrieved_strings 会变成一个字符串列表,每个字符串都包含一个单独的 JSON 对象。你可以在这里查看证明:http://ideone.com/Purpb

示例

下面这个字符串:

'{field1:"a",field2:"b"}{field1:"c",field2:"d"}{field1:"e",field2:"f"}'

会被转换成:

['{field1:"a",field2:"b"}', '{field1:"c",field2:"d"}', '{field1:"e",field2:"f"}']

这在我提到的 示例中得到了证明

23

这段代码可以把你的一串字符串中的“列表”解码成JSON对象:

from json import JSONDecoder

def loads_invalid_obj_list(s):
    decoder = JSONDecoder()
    s_len = len(s)

    objs = []
    end = 0
    while end != s_len:
        obj, end = decoder.raw_decode(s, idx=end)
        objs.append(obj)

    return objs

这里的好处是你和解析器相处得很好。因此,它会准确地告诉你错误出现的地方。

示例

>>> loads_invalid_obj_list('{}{}')
[{}, {}]

>>> loads_invalid_obj_list('{}{\n}{')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "decode.py", line 9, in loads_invalid_obj_list
    obj, end = decoder.raw_decode(s, idx=end)
  File     "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/decoder.py", line 376, in raw_decode
    obj, end = self.scan_once(s, idx)
ValueError: Expecting object: line 2 column 2 (char 5)

干净的解决方案(后面添加的)

import json
import re

#shameless copy paste from json/decoder.py
FLAGS = re.VERBOSE | re.MULTILINE | re.DOTALL
WHITESPACE = re.compile(r'[ \t\n\r]*', FLAGS)

class ConcatJSONDecoder(json.JSONDecoder):
    def decode(self, s, _w=WHITESPACE.match):
        s_len = len(s)

        objs = []
        end = 0
        while end != s_len:
            obj, end = self.raw_decode(s, idx=_w(s, end).end())
            end = _w(s, end).end()
            objs.append(obj)
        return objs

示例

>>> print json.loads('{}', cls=ConcatJSONDecoder)
[{}]

>>> print json.load(open('file'), cls=ConcatJSONDecoder)
[{}]

>>> print json.loads('{}{} {', cls=ConcatJSONDecoder)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/__init__.py", line 339, in loads
    return cls(encoding=encoding, **kw).decode(s)
  File "decode.py", line 15, in decode
    obj, end = self.raw_decode(s, idx=_w(s, end).end())
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/decoder.py", line 376, in raw_decode
    obj, end = self.scan_once(s, idx)
ValueError: Expecting object: line 1 column 5 (char 5)

撰写回答