Как использовать модуль 'json' для чтения по одному объекту JSON одновременно?
У меня есть мультигигабайтный файл JSON. Файл состоит из объектов JSON, каждый из которых содержит не более нескольких тысяч символов, но между записями нет разрывов строк.
Использование Python 3 и json
модуль, как я могу прочитать один объект JSON за один раз из файла в память?
Данные находятся в текстовом файле. Вот пример подобной записи. Фактические записи содержат много вложенных словарей и списков.
Запись в читаемом формате:
{
"results": {
"__metadata": {
"type": "DataServiceProviderDemo.Address"
},
"Street": "NE 228th",
"City": "Sammamish",
"State": "WA",
"ZipCode": "98074",
"Country": "USA"
}
}
}
Актуальный формат. Новые записи начинаются одна за другой без перерывов.
{"results": { "__metadata": {"type": "DataServiceProviderDemo.Address"},"Street": "NE 228th","City": "Sammamish","State": "WA","ZipCode": "98074","Country": "USA" } } }{"results": { "__metadata": {"type": "DataServiceProviderDemo.Address"},"Street": "NE 228th","City": "Sammamish","State": "WA","ZipCode": "98074","Country": "USA" } } }{"results": { "__metadata": {"type": "DataServiceProviderDemo.Address"},"Street": "NE 228th","City": "Sammamish","State": "WA","ZipCode": "98074","Country": "USA" } } }
3 ответа
Вообще говоря, помещение в файл нескольких объектов JSON делает этот файл недействительным, нарушает JSON. Тем не менее, вы все еще можете анализировать данные в кусках, используя JSONDecoder.raw_decode()
метод.
Следующее приведёт к завершенным объектам, так как парсер их найдет:
from json import JSONDecoder
from functools import partial
def json_parse(fileobj, decoder=JSONDecoder(), buffersize=2048):
buffer = ''
for chunk in iter(partial(fileobj.read, buffersize), ''):
buffer += chunk
while buffer:
try:
result, index = decoder.raw_decode(buffer)
yield result
buffer = buffer[index:]
except ValueError:
# Not enough data to decode, read more
break
Эта функция будет читать куски из данного файлового объекта в buffersize
куски, и есть decoder
Объект анализирует целые объекты JSON из буфера. Каждый разобранный объект передается вызывающей стороне.
Используйте это так:
with open('yourfilename', 'r') as infh:
for data in json_parse(infh):
# process object
Используйте это только в том случае, если ваши объекты JSON записаны в файл вплотную, без переносов между ними. Если у вас есть новые строки, и каждый объект JSON ограничен одной строкой, у вас есть документ JSON Lines, и в этом случае вы можете использовать Загрузка и анализ файла JSON с несколькими объектами JSON в Python.
Вот небольшая модификация решения Martijn Pieters, которая будет обрабатывать строки JSON, разделенные пробелами.
def json_parse(fileobj, decoder=json.JSONDecoder(), buffersize=2048,
delimiters=None):
remainder = ''
for chunk in iter(functools.partial(fileobj.read, buffersize), ''):
remainder += chunk
while remainder:
try:
stripped = remainder.strip(delimiters)
result, index = decoder.raw_decode(stripped)
yield result
remainder = stripped[index:]
except ValueError:
# Not enough data to decode, read more
break
Например, если data.txt
содержит строки JSON, разделенные пробелом:
{"business_id": "1", "Accepts Credit Cards": true, "Price Range": 1, "type": "food"} {"business_id": "2", "Accepts Credit Cards": true, "Price Range": 2, "type": "cloth"} {"business_id": "3", "Accepts Credit Cards": false, "Price Range": 3, "type": "sports"}
затем
In [47]: list(json_parse(open('data')))
Out[47]:
[{u'Accepts Credit Cards': True,
u'Price Range': 1,
u'business_id': u'1',
u'type': u'food'},
{u'Accepts Credit Cards': True,
u'Price Range': 2,
u'business_id': u'2',
u'type': u'cloth'},
{u'Accepts Credit Cards': False,
u'Price Range': 3,
u'business_id': u'3',
u'type': u'sports'}]
Если ваши документы JSON содержат список объектов, и вы хотите читать по одному объекту по одному, вы можете использовать итерационный JSON-анализатор ijson для этой работы. Он будет только читать больше содержимого из файла, когда ему нужно будет декодировать следующий объект.
Обратите внимание, что вы должны использовать его с библиотекой YAJL, иначе вы скорее всего не увидите увеличения производительности.
Тем не менее, если ваш файл не очень большой, чтение его полностью в память и последующий синтаксический анализ с помощью обычного модуля JSON, вероятно, все еще будет лучшим вариантом.