Eine vollständige, einfach lesbare Lösung, die einen Generator von einem normalen oder leeren iterierbaren Programm serialisieren kann, kann mit .encode() oder .itererencode() arbeiten. Schriftliche Prüfungen. Getestet mit 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), [])
Eingesetzte Lösungen: Vadim Pushtaev (unvollständig), user1158559 (unnötig kompliziert) und Claude (in einer anderen Frage, auch kompliziert).
Nützliche Vereinfachung ist:
- Es ist nicht notwendig, das erste Element gemächlich zu bewerten und es kann in
__init__
erfolgen, weil wir, dass der SerializableGenerator unmittelbar vor json.dumps aufgerufen werden kann, erwarten können. (gegen user1158559 Lösung)
- Es ist nicht notwendig, viele Methoden von NotImplementedError neu zu schreiben, da dies nicht alle Methoden wie
__repr__
sind.Es ist besser, den Generator auch in der Liste zu speichern, um aussagekräftige Ergebnisse wie [<generator object ...>]
zu erhalten. (gegen Claude). Die Standardmethoden __len__
und __bool__
funktionieren jetzt korrekt, um ein leeres und nicht leeres Objekt zu erkennen.
Ein Vorteil dieser Lösung ist, dass ein Standard-JSON Serializer ohne params verwendet werden kann. Wenn geschachtelte Generatoren unterstützt werden sollen oder wenn die Kapselung durch SerializableGenerator(iterator)
nicht wünschenswert ist, empfehle ich IterEncoder Antwort.
Wie wäre es, die Dokumente nur durch Einfügen von Kommas zu verketten? – bereal
Sie müssen das äußere Array jeder Datei entfernen. Das Entfernen des ersten und letzten Zeichens jeder Datei sollte funktionieren, aber ich möchte den JSON-Einzug steuern (und entfernen). –
Wie groß sind die Dateien eigentlich? könnte es sein, dass das Halten der vollständigen serialisierten Daten größer ist als Ihr Speicher? – Alex