Получение объектов JSON из текстового файла (с использованием Python)
У меня есть тысячи текстовых файлов, содержащих несколько объектов JSON, но, к сожалению, между объектами нет разделителя. Объекты хранятся в виде словарей, а некоторые из их полей сами являются объектами. Каждый объект может иметь переменное количество вложенных объектов. Конкретно, объект может выглядеть так:
{field1: {}, field2: "some value", field3: {}, ...}
и сотни таких объектов объединяются без разделителя в текстовом файле. Это означает, что я не могу использовать json.load()
или json.loads()
.
Любое предложение о том, как я могу решить эту проблему. Есть ли известный парсер для этого?
- Эквивалент Python сравнения Java ()
- Файловый объект Python позволяет закрыть файл, который уже закрыт
- Python: объект 'int' не подлежит расшифровке
- Перечислить преобразование объектов в integer
- имя объекта печати / экземпляра в python
Это расшифровывает ваш «список» объектов 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)
Себастьян Бласк имеет правильную идею, но нет смысла использовать регулярные выражения для такого простого изменения.
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
Как насчет чего-то вроде этого:
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 )
Решение
Насколько я знаю }{
не отображается в действительном 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"}']
как доказано в примере, который я упомянул .
Почему вы не загружаете файл в виде строки, замените все} {на}, {и окружаете все это с помощью []? Что-то вроде:
re.sub('\}\s*?\{', '\}, \{', string_read_from_a_file)
Или просто замените строку, если вы уверены, что у вас всегда есть} {без пробелов между ними.
В случае, если вы ожидаете, что} {произойдет также в строках, вы также можете разделить} {и оценить каждый фрагмент с помощью json.load, если вы получите сообщение об ошибке, фрагмент не был завершен, и вам нужно добавить следующий первый и т. д.
Как насчет чтения файла, увеличивающего счетчик каждый раз, когда {найден и уменьшает его, когда вы сталкиваетесь с}. Когда ваш счетчик достигнет 0, вы узнаете, что вы пришли к концу первого объекта, поэтому отправьте это через json.load и начните считать снова. Затем просто повторите до завершения.
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') '''
Предположим, вы добавили [к началу текста в файле и использовали версию json.load (), которая, обнаружив ошибку поиска {вместо ожидаемой запятой (или попадающей в конец файла) , выплюнуть только что законченный объект?
Замените файл этим нежелательным:
$ sed -i -e 's;}{;}, {;g' foo
Сделайте это на лету в Python:
junkJson.replace('}{', '}, {')
- В Python, учитывая URL-адрес текстового файла, какой самый простой способ прочитать содержимое текстового файла?
- SOAP suds и ужасная схема Тип Не найдена ошибка
- Многие экземпляры класса
- Можно ли загрузить объект в память на FTP с помощью Python?
- Как разрезать список объектов в ассоциации атрибутов объекта
- Список в классе не сбрасывается в файл
- Что такое переменная cls, используемая в классах python?
- Что более фундаментально: функции Python или объектные методы Python?
- В Python есть только один объект True и один False?
- Как удалить объект из списка внутри списка?
- Преобразование строкового представления списка объектов обратно в список в python