pythonでjsonのファイルを追記保存するの面倒やんけ!!(ndjsonの紹介)

#はじめに
pythonでjson形式でファイル保存するとき，次のようにdump関数を使います．
例として，毎日の体重を記録するjsonファイルを想定します．

```python:json形式でファイル保存
import json

# 体重をdict形式に入れる
weight_dict = {'date': '2021-01-01', 'weight': 58.3}

filename = 'weight.json'

with open(filename, 'w') as f:
    json.dump(weight_dict, f)
```

```json:weight.json
{"date": "2021-01-01", "weight": 58.3}
```

その後，jsonファイルにさらにデータを追記したい，となったとき，どうすればよいでしょうか．
例えば，次の日の体重データを同じjsonファイルに追記したい，という場合を考えてみましょう．
対策として次のような方法があります．

#対策1:単純に追記する
上のコード実行後に次のように実行します．

```python:対策1_追記する
weight_dict = {'date': '2021-01-02', 'weight': 58.5}

filename = 'weight.json'

with open(filename, 'a') as f:
    json.dump(weight_dict, f)
```

```json:weight.json
{"date": "2021-01-01", "weight": 58.3}{"date": "2021-01-02", "weight": 58.5}
```


これでもよいのですが，これでは<b>正しいjsonファイル形式になりません．</b>
というのも，

```json:正しいjsonファイル形式
[{"date": "2021-01-01", "weight": 58.3}, {"date": "2021-01-02", "weight": 58.5}]
```

なら正しい形式となるのですが，2つの(pythonでいう)辞書形式を並べただけでは，正しくなく，jsonファイルとして読み込みできません．
試しに次のようなコードを実行すると，エラーが生じます．

```python:jsonファイル(?)を読み込み
with open(filename, 'r') as f:
    read_data = json.load(f)
```

```txt:エラー内容
JSONDecodeError: Extra data: line 1 column 39 (char 38)
```

次のようにごり押しをすればいけないこともないですが，数値データが文字列になっています．

```python:対策1_ごり押し
with open(filename, 'r') as f:
    read_data_str = f.read()
    read_data = "[" + read_data_str.replace("\n", "").replace("}{", "},{") + "]"
    dict_data = eval(read_data)
print(dict_data)
```

```text:実行結果
[{'date': '2021-01-01', 'weight': 58.3},
 {'date': '2021-01-02', 'weight': 58.5}]
```

#対策2:いったんファイルをすべて読みこませてから再度書き込む
はじめに，で実行したコードの後に，以下のようなコードを実行します.

```python:対策2_読み込んで再度書き出し
weight_dict = {'date': '2021-01-02', 'weight': 58.5}

with open(filename, 'r') as f:
    read_data = json.load(f)
    
save_data = [read_data, weight_dict]

with open(filename, 'w') as f:
    json.dump(save_data, f)
```

```json:weight.json
[{"date": "2021-01-01", "weight": 58.3}, {"date": "2021-01-02", "weight": 58.5}]
```

となり，正しいデータを追記することができました！
もちろん，この方法で問題ないですし，今回の例だったら問題ないのですが，jsonファイルのファイルサイズが大型な場合，読み込み処理に時間がかかってしまいます．

#対策3:ndjsonを使う!
ここで，json形式を使うのではなく，jsonライクな形式を使おうと考えます．
対策1では，json形式をそのまま追記していました．この方法を少し変えて，
<b>json形式を追記するが，それぞれのデータ間に改行\nを加える</b>というのがndjson形式です．
ちなみにjson line形式とも呼ばれているそうです．
つまり，次のようなjsonライクなファイルで保存します．

```json:weight.ndjson
{"date": "2021-01-01", "weight": 58.3}
{"date": "2021-01-02", "weight": 58.5}
```

対策1で作成したjsonファイルとの違いは，改行しているかどうか，だけてす．
また，ファイルに追記するときも，改行コードとデータをただただ末尾に追記するだけなので，すべてのデータを読み込み→追記→書き込み，をする必要はありません．

使用方法は簡単，pythonではndjsonというそのまんまのパッケージがあるので，それを使うだけです．
[公式ドキュメント](https://pypi.org/project/ndjson/)

```txt
pip install ndjson
```



```python:対策3:ndjsonを使った場合
import ndjson

filename = 'weight.ndjson'

# 体重をdict形式に入れる
weight_dict = {'date': '2021-01-01', 'weight': 58.3}

with open(filename, 'a') as f:
    writer = ndjson.writer(f)
    writer.writerow(weight_dict)

# 追記したいデータ
weight_dict = {'date': '2021-01-02', 'weight': 58.5}

with open(filename, 'a') as f:
    writer = ndjson.writer(f)
    writer.writerow(weight_dict)
```

```json:weight.ndjson
{"date": "2021-01-01", "weight": 58.3}
{"date": "2021-01-02", "weight": 58.5}
```

読みこみもできます．

```python:ndjsonの読み込み
with open(filename) as f:
    data = ndjson.load(f)
```

```txt:実行結果
[{'date': '2021-01-01', 'weight': 58.3}, {'date': '2021-01-02', 'weight': 58.5}]
```

今回紹介した例では，2つのデータだけだったので，ndjsonを使うメリットがあまり実感出来なかったかもしればせんが，
例えば，for文を使って事前に長さの分からない大量のデータを書き込む場合，while True文を使って，1分おきに特定のサイトからデータを取得し，記録する，といった使い方をするときに便利です．