Analizar varios objetos json que están en una línea

Estoy analizando archivos que contienen objetos json. El problema es que algunos archivos tienen varios objetos en una línea. p.ej:

{"data1": {"data1_inside": "bla{bl\"a"}}{"data1": {"data1_inside": "blabla["}}{"data1": {"data1_inside": "bla{bla"}}{"data1": {"data1_inside": "bla["}} 

He creado una función que intenta analizar una subcadena cuando no quedan corchetes abiertos, pero puede haber corchetes en los valores. He intentado omitir valores con la comprobación del inicio y final de las comillas, pero también hay valores con comillas escapadas. ¿Alguna idea sobre cómo lidiar con esto?

Mi bash:

 def get_lines(data): lines = [] open_brackets = 0 start = 0 is_comment = False for index, c in enumerate(data): if c == '"': is_comment = not is_comment elif not is_comment: if c == '{': if not open_brackets: start = index open_brackets += 1 if c == '}': open_brackets -= 1 if not open_brackets: lines.append(data[start: index+1]) return lines 

Versión simple pero menos robusta:

 >>> import re >>> s = r'{"data1": {"data1_inside": "bla{bl\"a"}}{"data1": {"data1_inside": "blabla["}}{"data1": {"data1_inside": "bla{bla"}}{"data1": {"data1_inside": "bla["}}' >>> r = re.split('(\{.*?\})(?= *\{)', s) ['', '{"data1": {"data1_inside": "bla{bl\\"a"}}', '', '{"data1": {"data1_inside": "blabla["}}', '', '{"data1": {"data1_inside": "bla{bla"}}', '{"data1": {"data1_inside": "bla["}}'] 

Esto fallará si }{ está contenido en una cadena

Como otros sugirieron, podrías intentar analizar cada elemento. Si no es válido, deberíamos verificar este elemento junto con el siguiente.

Tenga en cuenta que r es el resultado del código anterior

 accumulator = '' res = [] for subs in r: accumulator += subs try: res.append(json.loads(accumulator)) accumulator = '' except: pass 

El problema es que no se puede dividir razonablemente por ningún carácter o secuencia de caracteres, porque esa secuencia siempre puede aparecer en una cadena como un valor de campo, por ejemplo, '{"data1": "}{"}{"data2":"foo"}' .

Si asumimos que cada subcadena en su archivo / cadena que sea JSON válida debe comenzar con '{' y terminar con '}' (por supuesto, en el caso general, también tendríamos que tratar con '[' y ']' personajes), aquí hay un enfoque de fuerza bruta:

 import json with open('input.txt') as inp: s = inp.read().strip() jsons = [] start, end = s.find('{'), s.find('}') while True: try: jsons.append(json.loads(s[start:end + 1])) except ValueError: end = end + 1 + s[end + 1:].find('}') else: s = s[end + 1:] if not s: break start, end = s.find('{'), s.find('}') for x in jsons: print(x) 

Manifestación:

 $ cat input.txt {"data1": {"data1_inside": "bla{bl\"a"}}{"data1": {"data1_inside": "blabla["}}{"data1": {"data1_inside": "bla{bla"}}{"data1": {"data1_inside": "bla["}} $ python json_linereader.py {u'data1': {u'data1_inside': u'bla{bl"a'}} {u'data1': {u'data1_inside': u'blabla['}} {u'data1': {u'data1_inside': u'bla{bla'}} {u'data1': {u'data1_inside': u'bla['}} 

Salida para s = '{"data1": "}{"}{"data2":"foo"}'

 {'data1': '}{'} {'data2': 'foo'} 

No he comprobado este código para todas las eventualidades con las pruebas unitarias, pero la idea debería ser clara.

Puedes usar el json raw_decoder! Esto permite la lectura de cadenas json con datos adicionales después del primer objeto json. Un ejemplo de uso sería:

 >>> dec = json.JSONDecoder() >>> json_str = '{"data": "Foo"}{"data": "BarBaz"}{"data": "Qux"}' >>> dec.raw_decode(json_str) ({u'data': u'Foo'}, 15) >>> dec.raw_decode(json_str[15:]) ({u'data': u'BarBaz'}, 18) >>> dec.raw_decode(json_str[33:]) ({u'data': u'Qux'}, 15) 

La primera parte de la tupla es el objeto json, la segunda es cuánta de la cadena se usó al leerla. Por lo tanto, un bucle como este le permitirá recorrer en iteración todos los objetos json en una cadena.

 dec = json.JSONDecoder() pos = 0 while not pos == len(str(json_str)): j, json_len = dec.raw_decode(str(json_str)[pos:]) pos += json_len # Do something with the json j here