この記事について
この記事は、生成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_NUMBER | NaN、Infinity など | Python固有の寛容な解釈を採用しない |
LINE_TOO_LONG | 1行が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が重複していないかという業務ルールの検査です。構文検査が通ったことと、データを信頼してよいことは同じではありません。役割を分けて設計することで、検査の根拠を説明しやすくなります。
