我有数千个包含多个JSON对象的文本文件,但不幸的是,对象之间没有分隔符。对象存储为字典,其中一些字段本身就是对象。每个对象可能具有可变数量的嵌套对象。具体而言,对象可能如下所示:
{field1: {}, field2: "some value", field3: {}, ...}
并且在文本文件中没有分隔符的情况下连接数百个这样的对象。这意味着我既不能使用json.load()
也不能使用json.loads()
。
有关如何解决此问题的任何建议。是否有一个已知的解析器来执行此操作?
答案 0 :(得分:22)
这将从字符串中解码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)
答案 1 :(得分:4)
塞巴斯蒂安·布拉斯克有正确的想法,但没有理由使用正则表达式进行这么简单的改变。
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
答案 2 :(得分:3)
据我所知}{
没有出现在有效的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"}']
在the example I mentioned中得到证实。
答案 3 :(得分:3)
这样的事情怎么样:
import re
import json
jsonstr = open('test.json').read()
p = re.compile( '}\s*{' )
jsonstr = p.sub( '}\n{', jsonstr )
jsonarr = jsonstr.split( '\n' )
for jsonstr in jsonarr:
jsonobj = json.loads( jsonstr )
print json.dumps( jsonobj )
答案 4 :(得分:2)
为什么不将文件作为字符串加载,替换所有} {with},{并用[]包围整个文件?类似的东西:
re.sub('\}\s*?\{', '\}, \{', string_read_from_a_file)
或者简单的字符串替换,如果你确定你总是有{}之间没有空格。
如果您希望} {也出现在字符串中,您也可以拆分} {并使用json.load评估每个片段,以防您收到错误,片段未完成且您必须添加在第一个旁边等等。
答案 5 :(得分:1)
每次找到{时,通过文件递增计数器怎么样,当遇到{时,递减计数器}。当你的计数器达到0时,你就会知道你已经到了第一个对象的末尾,所以通过json.load发送它并重新开始计数。然后重复完成。
答案 6 :(得分:1)
import json
file1 = open('filepath', 'r')
data = file1.readlines()
for line in data :
values = json.loads(line)
'''Now you can access all the objects using values.get('key') '''
答案 7 :(得分:0)
假设您在文件的文本开头添加了[并使用了json.load()的版本,当它检测到错误时找到{而不是预期的逗号(或命中结束)文件),吐出刚刚完成的对象?
答案 8 :(得分:0)
用其中的垃圾替换文件:
$ sed -i -e 's;}{;}, {;g' foo
在Python中动态执行:
junkJson.replace('}{', '}, {')