Полное удобное для чтения решение, которое может сериализовать генератор из обычного или пустого итерации, может работать с .encode() или .iterencode(). Письменные тесты. Испытано с Python 2.7, 3.0, 3.3, 3.6
import itertools
class SerializableGenerator(list):
"""Generator that is serializable by JSON
It is useful for serializing huge data by JSON
>>> json.dumps(SerializableGenerator(iter([1, 2])))
"[1, 2]"
>>> json.dumps(SerializableGenerator(iter([])))
"[]"
It can be used in a generator of json chunks used e.g. for a stream
>>> iter_json = ison.JSONEncoder().iterencode(SerializableGenerator(iter([])))
>>> tuple(iter_json)
('[1', ']')
# >>> for chunk in iter_json:
# ... stream.write(chunk)
# >>> SerializableGenerator((x for x in range(3)))
# [<generator object <genexpr> at 0x7f858b5180f8>]
"""
def __init__(self, iterable):
tmp_body = iter(iterable)
try:
self._head = iter([next(tmp_body)])
self.append(tmp_body)
except StopIteration:
self._head = []
def __iter__(self):
return itertools.chain(self._head, *self[:1])
# -- test --
import unittest
import json
class Test(unittest.TestCase):
def combined_dump_assert(self, iterable, expect):
self.assertEqual(json.dumps(SerializableGenerator(iter(iterable))), expect)
def combined_iterencode_assert(self, iterable, expect):
encoder = json.JSONEncoder().iterencode
self.assertEqual(tuple(encoder(SerializableGenerator(iter(iterable)))), expect)
def test_dump_data(self):
self.combined_dump_assert(iter([1, "a"]), '[1, "a"]')
def test_dump_empty(self):
self.combined_dump_assert(iter([]), '[]')
def test_iterencode_data(self):
self.combined_iterencode_assert(iter([1, "a"]), ('[1', ', "a"', ']'))
def test_terencode_empty(self):
self.combined_iterencode_assert(iter([]), ('[]',))
def test_that_all_data_are_consumed(self):
gen = SerializableGenerator(iter([1, 2]))
list(gen)
self.assertEqual(list(gen), [])
Используемые решения: Вадим Pushtaev (неполные), user1158559 (излишне сложным) и Claude (другой вопрос, также сложно).
Полезное упрощение является:
- Не нужен оценить первый элемент лениво и может быть это сделано в
__init__
потому, что мы можем ожидать, что SerializableGenerator может быть немедленно вызваны перед json.dumps. (против user1158559)
- Нет необходимости переписывать многие методы NotImplementedError, потому что это не все методы, такие как
__repr__
.Лучше хранить генератор и в списке, чтобы обеспечить значимые результаты, такие как [<generator object ...>]
. (против Клода). Способы по умолчанию __len__
и __bool__
теперь корректно распознают пустой и не пустой объект.
Преимущество такого решения заключается в том, что стандарт JSON сериализатор может быть использована без Params. Если вложенные генераторы должны поддерживаться или если инкапсуляция на SerializableGenerator(iterator)
нежелательна, я рекомендую ответить IterEncoder.
Как насчет текстовой конкатенации документов, вставляющих запятые между? – bereal
Вам нужно удалить внешний массив каждого файла. Удаление кулака и последнего символа каждого файла должно работать, но я хотел бы управлять (и удалять) отступ json. –
Насколько велики файлы на самом деле? может ли быть, что полная сериализованная информация больше вашей памяти? – Alex