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

Как использовать модуль "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" } } }
4b9b3361

Ответ 1

Вообще говоря, помещение в файл нескольких объектов 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:].lstrip()
             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.

Ответ 2

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

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

Говоря, что если ваш файл действительно большой, его полностью считывать в память, а затем анализировать его с помощью обычного модуля JSON, вероятно, будет лучшим вариантом.

Ответ 3

Вот небольшая модификация решения 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'}]