JSONファイルのリストを巨大なJSON配列に連結するにはどうすればよいですか?私は5000ファイルと550 000リストアイテムを持っています。
私の最初の試みは jq を使用することでしたが、jq -sが大きな入力用に最適化されていないようです。
jq -s -r '[.[][]]' *.js
このコマンドは機能しますが、完了するまでに時間がかかりすぎるので、Pythonでこれを解決したいと思います。
これが私の現在のコードです:
def concatFiles(outName, inFileNames):
def listGenerator():
for inName in inFileNames:
with open(inName, 'r') as f:
for item in json.load(f):
yield item
with open(outName, 'w') as f:
json.dump(listGenerator(), f)
私は得ています:
TypeError: <generator object listGenerator at 0x7f94dc2eb3c0> is not JSON serializable
RAMにすべてのファイルをロードしようとすると、LinuxのOOMキラーがトリガーされます。あなたはなにか考えはありますか?
list
から派生し、__iter__
メソッドをオーバーライドする必要があります。
import json
def gen():
yield 20
yield 30
yield 40
class StreamArray(list):
def __iter__(self):
return gen()
# according to the comment below
def __len__(self):
return 1
a = [1,2,3]
b = StreamArray()
print(json.dumps([1,a,b]))
結果は[1, [1, 2, 3], [20, 30, 40]]
です。
Simplejson 3.8.0以降、iterable_as_array
オプションを使用して、反復可能な配列を配列にシリアライズ可能にすることができます。
# Since simplejson is backwards compatible, you should feel free to import
# it as `json`
import simplejson as json
json.dumps((i*i for i in range(10)), iterable_as_array=True)
結果は[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
です
通常または空の反復可能オブジェクトからジェネレーターをシリアル化できる完全でシンプルな読み取り可能なソリューションは、.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_iterencode_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), [])
_
使用されているソリューション:Vadim Pushtaev(不完全)、user1158559(不必要に複雑)、および Claude (別の質問でも複雑)。
便利な簡略化は次のとおりです。
__init__
_で実行できます。これは、json.dumpsの直前にSerializableGeneratorを呼び出すことができるためです。 (user1158559ソリューションに対して)__repr__
_のようなものではないため、NotImplementedErrorによって多くのメソッドを書き換える必要はありません。 _[<generator object ...>]
_のような意味のある結果を提供するには、ジェネレータもリストに格納することをお勧めします。 (クロードに対して)。デフォルトのメソッド___len__
_および___bool__
_は、空のオブジェクトと空でないオブジェクトを正しく認識するようになりました。このソリューションの利点は、標準のJSONシリアライザーをパラメーターなしで使用できることです。ネストされたジェネレーターをサポートする必要がある場合、またはSerializableGenerator(iterator)
によるカプセル化が望ましくない場合は、 IterEncoder の回答をお勧めします。
受け入れられた答えに基づいて、ここに私が最終的に行ったStreamArrayがあります。 2つの嘘が含まれています。
self.__tail__
_は不変である可能性があるという提案len(StreamArray(some_gen))
は0または1です。
_class StreamArray(list):
def __init__(self, gen):
self.gen = gen
def destructure(self):
try:
return self.__head__, self.__tail__, self.__len__
except AttributeError:
try:
self.__head__ = self.gen.__next__()
self.__tail__ = self.gen
self.__len__ = 1 # A lie
except StopIteration:
self.__head__ = None
self.__tail__ = []
self.__len__ = 0
return self.__head__, self.__tail__, self.__len__
def rebuilt_gen(self):
def rebuilt_gen_inner():
head, tail, len_ = self.destructure()
if len_ > 0:
yield head
for elem in tail:
yield elem
try:
return self.__rebuilt_gen__
except AttributeError:
self.__rebuilt_gen__ = rebuilt_gen_inner()
return self.__rebuilt_gen__
def __iter__(self):
return self.rebuilt_gen()
def __next__(self):
return self.rebuilt_gen()
def __len__(self):
return self.destructure()[2]
_
使い捨てのみ!