PythonでJSONLの壊れた行番号を調べる:データを表示しない読み取り専用チェッカー

プログラミング・Web開発カテゴリを表すパンダのイラスト プログラミング・Web開発

この記事について
この記事は、生成AIを活用した自動生成フローで作成しています。JSON Linesの形式規則とPython標準ライブラリの仕様を参照し、架空データだけを使う読み取り専用の検査プログラムを作成しました。Linux / Python 3.13.5で単体テスト13件とCLIの正常・異常・ファイル不存在を確認しています。

検証ステータス:🧪 Linux / Python 3.13.5で13件の単体テストを2026-11-03に再実行しPASS。Windows/macOS・WordPress本番表示は未確認。

JSON Lines(JSONL)は、JSONのデータを1行ずつ並べる形式です。アプリケーションの実行履歴、データの受け渡し、AI関連の処理結果などで使われます。通常のJSONファイルのように全体を1つの配列にする必要がなく、1行ずつ追加・処理しやすいことが特徴です。

便利な一方で、1行だけ壊れたファイルをそのまま取り込むと、途中で処理が止まったり、失敗した行を読み飛ばしたまま成功と誤認したりします。特に業務ログでは、原因調査のためにファイル全体を画面やAIへ貼り付けると、内部の識別子や入力内容を意図せず公開するおそれがあります。そこで今回は、元のデータを変更せず、問題のある行番号と理由だけを報告する小さなPythonツールを試します。

何が分かるか、何は分からないか

このツールが確認するのは、各行がUTF-8として読めるか、JSONとして解釈できるか、空行や先頭のBOMが含まれていないか、という形式面です。入力値の正しさ、業務上の必須項目、日付の妥当性、個人情報の有無までは判定しません。たとえば {"price":-1} はJSONとしては有効ですが、金額のルールに反するかどうかは別の検査が必要です。

また、プログラムは各行を逐次処理しますが、1行が極端に長い場合に無制限でメモリを使わないよう、1行1MiBを超えたらそこで停止する独自の安全策を設けています。この制限はJSON Linesの規格ではなく、教材としての資源保護です。検査を途中で終えた場合、表示されたエラー数はファイル全体の総数とは限りません。

前提環境と用意するもの

今回の実測環境はLinux上のPython 3.13.5です。追加パッケージ、ネットワーク接続、管理者権限は不要です。Windowsなら py -3、macOSやLinuxなら python3 など、Pythonの起動コマンドは環境に合わせて読み替えてください。Windows/macOSでの実行は今回確認していないため、全環境で同一の表示を保証するものではありません。

実際に動かすコードとテストは、末尾のGitHubサンプルに置いています。check_jsonl.py は読み取り専用の本体、test_check_jsonl.py は一時ディレクトリを使う単体テストです。実際の業務ログではなく、まず自分で作ったダミーファイルを対象にしてください。サンプルにあるテストデータ生成コマンドは新しい作業フォルダで実行し、既存ファイルを上書きしないようにします。

まず試す:2行目だけが壊れたJSONL

新しい作業ディレクトリにサンプルの check_jsonl.py を置き、次のコマンドで架空の入力を作ります。1行目と3行目は有効なJSON、2行目は {bad} というJSONとして不正な文字列です。

python3 -c "from pathlib import Path; Path('demo.jsonl').write_text('{\"id\":1}\n{bad}\n{\"id\":3}\n', encoding='utf-8')"
python3 check_jsonl.py demo.jsonl

Linux / Python 3.13.5で確認した結果は次のとおりでした。

LINE=2 ISSUE=INVALID_JSON
STATUS=INVALID VALID=2 READ=3 ERRORS=1

コマンドの終了コードは 2 です。LINE=2 は物理的な2行目に問題があったこと、VALID=2 は2行がJSONとして解釈できたことを示します。READ=3 は3行を読み取ったという意味で、成功行数ではありません。ERRORS=1 は今回の検査で見つけた問題数です。ログの本文や値は出力していません。

これを自動処理に組み込むときは、画面に INVALID と表示されたかどうかだけでなく、終了コードも確認します。正常は0、内容の不正は2、ファイルを開けない場合は3と区別しています。存在しないファイルを検査した場合は、入力パスを出力せず STATUS=READ_ERROR ERROR_TYPE=FileNotFoundError のように例外の種類だけを表示します。失敗を成功として次工程へ渡さないことが目的です。

なぜ1行ずつ読むのか

通常のJSONは、ひとまとまりの値を json.load() などで読みます。一方、JSON Linesは「1行につき1つのJSON値」という構造です。複数行をまとめて1つのJSON文書として読み込もうとすると、最初の値の後に別の値が現れた時点でエラーになります。したがって、ファイルを行ごとに読み、各行を json.loads() に渡す方が、どこで壊れたかを説明しやすくなります。

教材ではファイルをバイナリモードで開き、行を読み込んでからUTF-8として厳密にデコードします。これにより、文字コードが壊れている場合と、文字列としては読めるがJSONの構文が間違っている場合を区別できます。エラーを報告するときも、入力内容そのものや例外に含まれ得るデータ断片を表示せず、INVALID_UTF8、INVALID_JSON のような固定の分類名に置き換えます。

さらに、Pythonの json.loads() は既定では NaN や Infinity を受け入れる場合があります。しかし、標準的なJSONの数値としては扱えません。この教材は parse_constant を指定し、そうした値を NONFINITE_NUMBER として拒否します。これは「Pythonで読めた」ことと「他のシステムにも渡せるJSONである」ことを混同しないための対策です。

1か所変える:不正な行を null にする

先ほどの2行目を {bad} から null に変更してみてください。null はJSONとして有効な値なので、3行すべてが形式上は正常になります。オブジェクトでなければ不正、という判断はしていません。JSON Linesでは配列、文字列、数値、真偽値、null も1行の値として認められています。

反対に、2行目を何も書かない空行にすると、BLANK_LINE が報告されます。ファイル末尾の改行は「次の空レコード」を意味しませんが、行と行の間に実際の空行がある場合は不正です。この違いは、ログの件数を数えるときにも重要です。改行文字を単純に数える方法と、JSONとして有効なレコードを数える方法は同じではありません。

Windowsで一般的なCRLF(\r\n)も形式上は扱えます。また、最終行に改行がない場合でも、その行が有効なJSONなら受け入れます。これらは単体テストでも確認しました。UTF8_BOM は、ファイル先頭にBOMがある場合の検出結果です。BOMが付いた行の内容をJSONとして読めても、ファイル全体の判定は INVALID にします。

失敗したときの切り分け

表示最初に確認することこのツールの動作
INVALID_JSON引用符・カンマ・波括弧などの構文行番号だけ報告。自動修正しない
INVALID_UTF8入力の実際の文字コード別の文字コードを推測して読み直さない
BLANK_LINEログの途中に空行が入っていないか空行を無視して成功扱いしない
UTF8_BOMファイル先頭のBOM形式違反を報告する
NONFINITE_NUMBERNaN、Infinity などPython固有の寛容な解釈を採用しない
LINE_TOO_LONG1行が1MiBを超えていないかメモリ保護のため検査を中断
READ_ERRORファイルの存在と読み取り権限終了コード3で終了

業務のログには、顧客情報や認証情報が含まれる場合があります。エラーのある行をそのまま標準出力へ表示する方式は、問題箇所を見つけやすい反面、監視基盤やチャットへ機密情報が転記される危険があります。今回は行番号とエラー分類だけにしたため、原因の詳細を調べる際には、アクセス制御された元データを担当者が別途確認する必要があります。安全と診断のしやすさの両方を考えて使い分けてください。

既存のJSONL記事と、今回の位置付け

このサイトには、PowerShellでJSONLログを追記しながら検査する記事がすでにあります(2026年10月10日の関連記事)。今回の目的は同じJSONLの入門を繰り返すことではありません。Python標準ライブラリで既存ファイルを一切変更せずに検査し、データ本文を出力しないこと、UTF-8/BOM・非標準数値・行長制限・終了コードを独立したテストで確かめることに絞っています。ログの生成や追記が目的なら既存のPowerShell記事、受領したJSONLを取り込み前に点検したいなら今回のサンプル、という使い分けです。

テスト結果と、まだ確認していないこと

2026-11-03にGitHubの実ファイルとGit blob SHAが一致するコードを隔離環境へ展開し、python3 -m unittest discover -p 'test_*.py' -v を再実行して13件がPASS、失敗0件でした。正常な複数行、CRLFと最終改行なし、空行、不正JSON、不正UTF-8、BOM、NaN、空ファイル、1行の長さ上限、エラー数上限、CLIの正常・不正・ファイル不存在を確認しています。python3 -m py_compile check_jsonl.py test_check_jsonl.py による構文検査も成功しました。

CLIを直接実行した結果は、不正な3行入力が終了コード2、正常な2行入力が終了コード0、存在しないファイルが終了コード3でした。これらはLinux / Python 3.13.5での実測です。Codex CLIによる実装・検証は行っていません。Windows/macOSの実機、巨大ファイルでの長時間運転、権限の異なる実環境、独立した別AIによる品質審査は未実施です。GitHub Actionsは請求・支出制限でRunnerが起動しないため、今回の合否判定に使っていません。

仕事で使うときの判断

最初は本番ログを直接処理するのではなく、架空データと読み取り権限だけで動作を確認します。検査結果が不正なら取り込みを中断し、元ファイルを保全してください。自動修復や不正行の削除は別工程に分けると、障害原因の追跡がしやすくなります。エラー数上限で途中停止することがあるため、ERRORS=1 だけを見て「問題は1件しかない」と判断しないことも大切です。

このサンプルは「JSONLを安全に受け入れるための最初の関門」です。次に必要になるのは、JSONの中身が期待する項目を持つか、値の型が正しいか、時刻やIDが重複していないかという業務ルールの検査です。構文検査が通ったことと、データを信頼してよいことは同じではありません。役割を分けて設計することで、検査の根拠を説明しやすくなります。

公式情報・一次情報

GitHubサンプル

文書情報

記事タイトル
PythonでJSONLの壊れた行番号を調べる:データを表示しない読み取り専用チェッカー
作成日
更新日
Source URL
https://papanda925.com/?p=18463

ライセンス: 本記事のうち、当サイトが権利を有する本文・自作図表は、特記なき限り CC BY 4.0 で利用できます。生成AIを活用して作成・編集した内容を含みます。コードについて、別途ライセンス表示またはリンク先GitHubリポジトリのライセンスがある場合は、その条件を優先します。引用・第三者資料・画像・商標等は本ライセンスの対象外です。 利用ポリシー

タイトルとURLをコピーしました