やりたいこと
大きなサイズのjsonファイルをjsonモジュールで読むと処理にかなり時間がかかる。
ijsonモジュールを使うと大きなサイズのjsonファイルが一部ずつ読める。しかし、これらの中から検索を行うのは時間がかかる。
そこで、もともとのjsonファイルに含まれている要素単位で、jsonファイルを分割したい。
分割後のファイルはjsonモジュールで読み込めるようにしたい。
前提となるjsonファイルの構成
以下のファイル(test.json)を前提とする。hoge1の要素はarrayである。arrayに含まれている各objectの構造は同一である。
{"hoge1":
[
{"hoge2":
{"hoge3":[{"hoge4":"fuga1","hoge6":true},{"hoge8":"fuga3"}]
},
"hoge10":"fuga4"
},
{"hoge2":
{"hoge3":[{"hoge4":"fuga5","hoge6":false},{"hoge8":null}]
},
"hoge10":"fuga8"
}
]
}
書いたコード
import ijson
def save_as_json(string, item,file_path):
with open(file_path,"w") as f:
f.write('{"%s": %s}' % (string,str(item).replace("'",'"').replace("True",'true').replace("False",'false').replace("None",'null')))
with open("test.json","r") as j:
items = ijson.items(j,"hoge1.item.hoge2")
i=1
for item in items:
json_file = "%s.json" % i
save_as_json("hoge2",item,json_file)
i += 1
コードの意図
ijson.itemsの第2引数にドット区切りで名前を羅列すると、その名前で特定される値を取得することができる。arrayについては該当する位置にitemと入れれば、リストで取得できる。
ijson.items(j, "hoge1.item.hoge2")
上記で各hoge2の値をリストで取得できる。
f.write({"%s": %s} % (string,...))
ファイルに書き込む形式をjson形式とするため、{ "": }という形式とした。
値については、str(item)で文字列化した。ただ、文字列がシングルクォーテーションでくくられているため、
replace("'",'"')でダブルクォーテーションに変換した。また、jsonのtrue、false、nullは、str(item)の際にTrue,False,Noneとなってしまうため、replace("True",'true').replace("False",'false').replace("None",'null')で強引に変換した。(意図しない変換を防ぐためにはもう少し丁寧な変換をした方がよいかも)
結果
できたファイルのうち1.jsonファイルをjsonモジュールで読み込めるかを試してみた。
import json
with open("1.json","r") as nj:
item = json.load(nj)
print(item)
以下の通り出力されたので、ちゃんと読めているっぽい。
{'hoge2': {'hoge3': [{'hoge4': 'fuga1', 'hoge6': True}, {'hoge8': 'fuga3'}]}}
備考
保存ファイル名、ディレクトリ名などをそれぞれの値と関連付ければ、(例えば、fuga1/fuga3.jsonのようにすれば、検索が容易になると思われる。