はじめに
自然言語処理100本ノックの3章20番の問題を解いているときに詰まったエラー。
Pythonでjson形式の外部ファイルを読み込む場合、多くの場合json.load(fp)
を使うと思う。
最初そのやり方を試みたところ次のようなエラーが出力された。
json.decoder.JSONDecodeError: Extra data: line 2 column 1 (char 27837)
正直エラーメッセージからはよくわからない部分が多かったのだが、APIリファレンスを調べると解決策が見つかった。
対策
今回は原因より先に対策。
json.load()
はjsonモジュールにload
メソッドが直接定義されているのだが、他にもJSONDecoder()
クラスがある。
このクラスメソッド、JSONDecoder.raw_decode()
で使うとなんとかなりそうとのこと、実際うまく行った。
raw_decode()とは?
Python3のリファレンスに次のように説明がある。
s (str インスタンスで JSON 文書で始まるもの) から JSON 文書をデコードし、Python 表現と s の文書の終わるところのインデックスからなる 2 要素のタプルを返します。 このメソッドは後ろに余分なデータを従えた文字列から JSON 文書をデコードするのに使えます。
つまり不完全なjson形式のファイルに使用することが出来るのだ。
正確には不完全なjsonファイル
ではなく、jsonlines
形式のファイルを想定指定のだと思う。
jsonlinesファイルで実験してみる
次のようなjsonが\n
区切りで複数行並んだファイルを用意する。
{"1": "test"} {"2": "stage"} {"3": "dev"} {"4": "prod"}
json.loadでデコードしてみる。
>>> import json >>> with open('jsonlines.json', 'r') as f: ... res = json.load(f) ... Traceback (most recent call last): File "<stdin>", line 2, in <module> File "/Users/******/.pyenv/versions/3.7.5/lib/python3.7/json/__init__.py", line 296, in load parse_constant=parse_constant, object_pairs_hook=object_pairs_hook, **kw) File "/Users/******/.pyenv/versions/3.7.5/lib/python3.7/json/__init__.py", line 348, in loads return _default_decoder.decode(s) File "/Users/*****/.pyenv/versions/3.7.5/lib/python3.7/json/decoder.py", line 340, in decode raise JSONDecodeError("Extra data", s, end) json.decoder.JSONDecodeError: Extra data: line 2 column 1 (char 14)
先ほどと同じエラーが発生した。
JSONDecoder.raw_decode()でデコードしてみる
>>> res = [] >>> decoder = json.JSONDecoder() >>> with open('jsonlines.json', 'r') as f: ... line = f.readline() ... while line: ... res.append(decoder.raw_decode(line)) ... line = f.readline() ... >>> for i, d in enumerate(res): ... print("{}: {}".format(i, d)) ... 0: ({'1': 'test'}, 13) 1: ({'2': 'stage'}, 14) 2: ({'3': 'dev'}, 12) 3: ({'4': 'prod'}, 13)
無事読み込めた。
原因
結果を見たところ、複数のjsonデータが区切り文字無しで連続していたようだった。
つまりjsonlinesもどきのような状態になっていた。
おわりに
こうやって見ると色々知らないメソッドがあると感じた。
たまにはリファレンスをざっと眺めてみるのも大事かもしれない。