Подтвердить что ты не робот

Получение объектов JSON из текстового файла (с использованием Python)

У меня есть тысячи текстовых файлов, содержащих несколько объектов JSON, но, к сожалению, между объектами нет разделителя. Объекты хранятся в виде словарей, а некоторые из их полей сами являются объектами. Каждый объект может иметь переменное количество вложенных объектов. Конкретно, объект может выглядеть так:

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

и сотни таких объектов объединены без разделителя в текстовом файле. Это означает, что я не могу использовать json.load() и json.loads().

Любое предложение о том, как я могу решить эту проблему. Есть ли известный парсер для этого?

4b9b3361

Ответ 1

Это расшифровывает ваш "список" объектов 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)

Ответ 2

Себастьян Бласк имеет правильную идею, но нет причин использовать регулярные выражения для такого простого изменения.

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

Ответ 3

Решение

Насколько я знаю, }{ не отображается в действительном JSON, поэтому следующее должно быть совершенно безопасным при попытке получить строки для отдельных конкатенированных объектов (txt - это содержимое вашего файла). Для этого не требуется импорт (даже модуля re):

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

или если вы предпочитаете понимание списков (как отметил Дэвид Цвиккер в комментариях), вы можете использовать его так:

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"}']

как доказано в примере, о котором я говорил.

Ответ 4

Как насчет чего-то вроде этого:

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 )

Ответ 5

Почему вы не загружаете файл как строку, замените все} {на}, {и окружаете все это с помощью []? Что-то вроде:

re.sub('\}\s*?\{', '\}, \{', string_read_from_a_file)

Или простая строка замените, если вы уверены, что у вас всегда есть} {без пробелов между ними.

Если вы ожидаете, что} {произойдет и в строках, вы также можете разбить} {и оценить каждый фрагмент с помощью json.load, в случае, если вы получите ошибку, фрагмент не был завершен, и вам нужно добавить рядом с первым и т.д.

Ответ 6

Как прочитывать файл, увеличивая счетчик каждый раз, когда {найден и уменьшает его, когда вы сталкиваетесь с}. Когда ваш счетчик достигнет 0, вы узнаете, что вы пришли к концу первого объекта, поэтому отправьте это через json.load и начните считать снова. Затем просто повторите до завершения.

Ответ 7

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') '''

Ответ 8

Предположим, вы добавили [к началу текста в файле и использовали версию json.load(), которая, когда обнаружила ошибку нахождения {вместо ожидаемой запятой (или попала в конец файл), выплюнуть только что законченный объект?

Ответ 9

Замените файл этим нежелательным файлом в нем:

$ sed -i -e 's;}{;}, {;g' foo

Сделайте это на лету в Python:

junkJson.replace('}{', '}, {')