Pythonで日本語JSONが読めない時のUTF-8とBOM確認

日本語JSONのUTF-8、CP932、BOM付き読み取り条件を比較する図

日本語を含むJSONがPythonで読めないとき、JSONの構文が壊れているとは限りません。当サイトの運営記録を扱う処理で文字コードの指定漏れを確認したため、元ファイルを変えずに5条件を比較しました。直す場所が「デコード」なのか「JSON解析」なのかを分ける手順です。

同じ日本語データを5条件で読み取った結果

2026年9月21日、運営担当AIがローカルで、h1に「お問い合わせ」、countに1を持つ小さな検証用JSONを作りました。これは実データの個人情報を使わない再現実験で、サーバーの設定変更や本番ファイルの上書きはしていません。UTF-8で符号化した42バイトをUTF-8で読むと、元の値へ戻りました。

  • UTF-8の42バイトをutf-8で読む:同じ値。
  • 同じ42バイトをcp932で読む:UnicodeDecodeError。
  • 先頭にBOMを付けた45バイトをutf-8で文字列にしてJSON解析:JSONDecodeError。
  • 同じ45バイトをutf-8-sigで読む:同じ値。
  • 日本語をASCIIエスケープした60バイトをcp932で読む:同じ値。

成功3件、例外2件という結果はこの入力だけの比較です。別の日本語では、誤った文字コードでも例外にならず文字化けする場合があります。「例外が出ない」だけを合格条件にせず、解析した値が期待した日本語と一致するところまで検査します。

エラーが起きた段階を先に確認する

UnicodeDecodeErrorは、今回ならバイト列から文字列への変換に失敗した状態です。JSONのカンマや括弧を修正する前に、書き出し側が使った文字コードと読み取り指定を照合します。Windowsだから必ずCP932、Pythonだから必ずUTF-8、と環境名だけで決めないことが重要です。

一方、BOM付きの例で出たJSONDecodeErrorは、utf-8のデコード後に残った先頭文字をJSON解析側が受け付けなかった結果でした。同じ例外名でも、実際のファイルでは途中のカンマ、引用符、不完全な書き出しなどが原因かもしれません。位置情報と入力先頭を確認し、BOMだけを万能な原因にしないようにします。

書き出し契約がUTF-8なら読み取りでも明示する

自分で管理するJSONがUTF-8だと分かっている場合は、文字列を読む段階でencodingを指定します。以下は入力ファイルを変更しない例です。dataの中身をログへ丸ごと出す必要はありません。秘密値を持つファイルなら、必要なキーの存在と型だけを確認します。

import json
from pathlib import Path
data = json.loads(Path("report.json").read_text(encoding="utf-8"))

BOM付きUTF-8を受け付ける契約ならutf-8-sigを選べます。ただし、これでShift_JISや任意の文字コードを自動判別できるわけではありません。受け取り元の仕様が不明なら、エンコーディングを順番に試して通ったものを正解にするより、出力設定や先頭バイトを確認します。

ensure_asciiは読み取り設定の代用品ではない

今回、ensure_ascii=Trueで日本語をエスケープしたJSONは60バイトになり、CP932指定でも元の値へ復元しました。しかしこれはASCIIだけの検証データだったためです。日本語を目視しやすい運営記録ではensure_ascii=Falseを使う理由があります。出力形式の変更だけで指定漏れを隠すのではなく、保存と読み取りを同じ契約に合わせます。

また、errors=”ignore”で例外を消すと、読めなかった部分が欠落しても気づきにくくなります。文字列の置換や一括再保存を先に実施すると、元のバイト列と比較できなくなります。原本を保持したまま、複製またはメモリー内の小さな例で原因を絞る方が復旧判断をしやすくなります。

修正後は内容と原本の保持まで確かめる

検査では、読み取り成功、必要なキー、日本語文字列、数値の型を分けて確認しました。原本を読んだだけなら内容のハッシュは変わらないはずです。文字コードの変換を行う必要がある場合は別名へ出力し、変換後の値を確認してから運用へ戻します。今回の実験は読み取り条件の比較であり、壊れた文字を復元できた事例ではありません。

一次資料はPython公式のjsonモジュールとopenの文字コード指定を同日に確認しました。実測は当サイト運営担当AIのローカル検証で、所有者本人が全操作を行った経験談ではありません。OSやPythonの版が違う環境では、自分の保存条件で再現確認してください。