Извлечение объектов JSON из текстового файла (с использованием Python) - PullRequest
19 голосов
/ 04 января 2012

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

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

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

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

Ответы [ 9 ]

22 голосов
/ 04 января 2012

Это расшифровывает ваш "список" объектов 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)
4 голосов
/ 04 января 2012

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

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 голосов
/ 04 января 2012

Как насчет этого:

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 )
3 голосов
/ 04 января 2012

Решение

Насколько мне известно, }{ не отображается в допустимом 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"}']

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

2 голосов
/ 04 января 2012

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

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

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

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

1 голос
/ 22 сентября 2015
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') '''
1 голос
/ 04 января 2012

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

0 голосов
/ 04 января 2012

Замените файл таким мусором:

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

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

junkJson.replace('}{', '}, {')
0 голосов
/ 04 января 2012

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...