API連携のたびにJSONエンコードで日本語が化ける、例外が出る、どのオプションが正解か分からない…。そんな悩みを一気に解決します。
本記事は基礎→言語別の書き方→失敗例と対処→大規模データの最適化→セキュリティまで、現場でそのまま使える実践で使える知識を徹底解説します。
この記事は以下のような人におすすめ!
- JSONエンコードとは何か知りたい人
- JSONエンコード後に文字化けする理由が知りたい
- 言語ごとに最適解が分からない
目次
JSONエンコードとは何か
「JSONエンコード」とは、プログラム内のデータ(オブジェクトや配列、数値、文字列など)を、JSON(JavaScript Object Notation)という汎用的なデータ形式の文字列に変換することです。
つまり、アプリが内部で持っている生のデータを、外部のサービスや別のプログラムと安全かつ機械的にやり取りできる“共通言語”に整える処理を指します。
したがって、JSONエンコードはデータ連携・API通信・設定保存の基礎となる重要なステップです。
なお、「エンコード」は「暗号化」や「圧縮」とは目的が異なります。エンコードは“表現形式を変える”だけで、情報の秘匿やサイズ削減を直接の目的にしていません。
だから、混同しないことが大切です。
1-1. JSONとエンコード/デコードの関係
JSONは、人間に読みやすく、機械にとっても解析しやすい軽量なデータ表現です。
エンコードは“内部データ → JSON文字列”、デコードはその逆で“JSON文字列 → 内部データ”に戻す処理です。
つまり、エンコードとデコードは常に対で考えると理解が早まります。
1-1-1. JSONエンコードの位置づけ(なぜ必要か)
JSONエンコードが必要になる背景は明快です。アプリやサービス同士は、内部メモリの構造を直接渡し合うことはできません。
だからこそ、共通フォーマットのJSON文字列に変換して受け渡しします。
その結果、異なる言語・環境(ブラウザ、サーバー、モバイルアプリ、サーバーレスなど)の間でも、データの意味を崩さずに共有できます。
1-1-2. エンコードとデコードの違い(往復可能性を理解する)
エンコードとデコードの役割は明確に分かれています。次の表で整理すると、頭に残りやすくなります。
用語 | 目的 | 入力 | 出力 | 代表的な関数例 |
---|---|---|---|---|
JSONエンコード | データを共有可能な文字列表現へ変換 | オブジェクト・配列・数値・文字列 | JSON文字列 | JavaScript: JSON.stringify / PHP: json_encode / Python: json.dumps |
JSONデコード | 文字列をプログラムで扱える形に戻す | JSON文字列 | オブジェクト・配列など | JavaScript: JSON.parse / PHP: json_decode / Python: json.loads |
つまり、エンコードとデコードは“行き来できる”ことが前提です。なぜなら、通信では送る側がエンコードし、受け取る側がデコードするからです。
したがって、どちらか片方だけを理解しても、実運用では困ってしまいます。
1-1-3. よくある誤解と注意点
- エンコードは暗号化ではない
情報を秘匿する機能はありません。必要なら別途、暗号化や認証を組み合わせます。 - 文字化けの多くは文字コード不一致
JSONは原則UTF-8で扱うと安定します。途中で別文字コードが混ざると崩れます。 - 循環参照はエンコードできないことがある
自分自身を参照するようなオブジェクトは、標準のJSONエンコードでは表現できません。 - 日付やバイナリは“そのまま”では表現しづらい
文字列(ISO 8601など)やBase64などの表現へ変換してからエンコードします。
1-2. よく使われる場面(API通信、設定保存、外部サービスとのやりとりなど)
実務で「JSONエンコード」が登場する場面は多岐にわたります。
だからこそ、どんな局面で使うのかを知っておくと、設計やデバッグで迷いにくくなります。
1-2-1. API通信(クライアント ↔ サーバー)
- フロントエンドからバックエンドへリクエストを送るとき、ボディをJSONエンコードして送信します。
- バックエンドは受信したJSON文字列をデコードして、内部の処理に渡します。
- その結果、言語やフレームワークが違っても同じ形でデータ交換ができます。
よくあるデータのやり取り例(概念):
- 認証情報やユーザー入力フォームの内容
- フィルタ条件やページネーションのパラメータ
- 決済・在庫・配送など複数システム間のやり取り
1-2-2. 設定保存・ローカル保存・ログ
- アプリの設定値をJSONファイルとしてディスクに保存しておくと、人間にも読みやすく、変更管理もしやすいです。
- ブラウザの
localStorage
やモバイルアプリの軽量ストレージに、JSONエンコードした文字列で保存しておくと、後で簡単に復元できます。 - 構造化ログをJSONで出力すると、ログ収集基盤での検索・集計が容易になります。
1-2-3. 外部サービス連携(Webhook・メッセージング・キュー)
- 外部サービスへのWebhook通知や、メッセージキューに積むイベントデータは、JSONエンコードされた文字列で送受信するのが一般的です。
- したがって、イベントスキーマ(プロパティ名・型・必須/任意)の合意が非常に重要になります。スキーマが定まっていれば、受け手のデコードもスムーズです。
補足:JSONエンコードをうまく使うためのチェックリスト
- 文字コードはUTF-8を前提に整える
- 循環参照や巨大データはエンコード前に構造を見直す
- 日付やバイナリは表現ルール(ISO 8601、Base64など)をチームで統一する
- 送受信双方でスキーマ(項目名・型・null許容)を明文化する
- エラー時の挙動(失敗時の戻り値や例外)を確認してログを残す
各言語での JSON エンコード方法
実務でつまずきやすいのは、「言語ごとに JSONエンコード の仕様や落とし穴が微妙に違う」点です。
ここでは主要言語の“最短ルート”と“ハマりどころ”をまとめます。
つまり、まずは正しい書き方を押さえ、ついで代表的なオプションやエラー回避を覚える、という順番で理解を固めましょう。
2-1. JavaScript(JSON.stringify)によるエンコード
JavaScriptでは JSON.stringify
が標準的な JSONエンコード 手段です。ブラウザ・Node.js いずれでも同様に使えます。
2-1-1. 最短の使い方(まずはこれだけ)
const data = { id: 1, name: “Taro”, tags: [“json”, “encode”] };
const json = JSON.stringify(data);
console.log(json); // {“id”:1,”name”:”Taro”,”tags”:[“json”,”encode”]}
この一行で、オブジェクトが JSON文字列 に変換されます。つまり、API通信のリクエストボディやローカル保存にそのまま使えます。
2-1-2. 整形(インデント)とフィルタ(replacer)
読みやすい整形出力や、特定キーだけを JSONエンコード したいときは以下です。
// 第2引数: replacer(null なら全件), 第3引数: space(インデント)
JSON.stringify(data, null, 2);
// 特定キーだけを含める
JSON.stringify(data, [“id”, “name”], 2);
// カスタム変換(値を書き換える)
JSON.stringify(data, (key, value) => key === “id” ? String(value) : value, 2);
replacer
は配列(許可キー)か関数(値の加工)を受け取れます。space
に数字を渡すとインデント幅になります。したがって、ログや設定ファイルを“人が読む”用途で便利です。
2-1-3. ハマりがちな点(undefined・関数・循環参照)
挙動 | 結果 | 対策の例 |
---|---|---|
undefined / 関数 / Symbol の値 | キー値では無視、配列要素では null | 事前に取り除く/文字列化する |
循環参照(自分を参照する構造) | TypeError を投げる | 参照を断つ/循環をID化する/ライブラリを使う |
Date | ISO文字列に自動変換(toJSON ) | 必要に応じてフォーマット統一 |
BigInt | 例外(変換不可) | 文字列に変換して保持する |
つまり、循環参照と BigInt はとくに注意です。その結果、事前の正規化処理が安定運用の鍵になります。
2-2. PHP(json_encode 関数)によるエンコード
PHPでは json_encode
が JSONエンコード の標準関数です。オプションが豊富で、文字化け対策や整形出力を細かく制御できます。
2-2-1. 最短の使い方(まずはこれだけ)
<?php
$data = [‘id’ => 1, ‘name’ => ‘太郎’, ‘tags’ => [‘json’, ‘encode’]];
$json = json_encode($data, JSON_UNESCAPED_UNICODE);
echo $json; // {“id”:1,”name”:”太郎”,”tags”:[“json”,”encode”]}
デフォルトでは日本語が \uXXXX
にエスケープされます。
つまり、人が読む場面では JSON_UNESCAPED_UNICODE
を付けるのが定番です。
2-2-2. よく使うフラグと深さ(depth)
json_encode(
$data,
JSON_UNESCAPED_UNICODE // 日本語をそのまま
| JSON_UNESCAPED_SLASHES // スラッシュをエスケープしない
| JSON_PRETTY_PRINT // 整形出力(改行・インデント)
| JSON_THROW_ON_ERROR, // 失敗時に例外を投げる
512 // 最大深さ(デフォルト512)
);
代表的なフラグの意味は以下です。
フラグ | 目的 |
---|---|
JSON_UNESCAPED_UNICODE | 日本語を \u エスケープしない |
JSON_UNESCAPED_SLASHES | / のエスケープをしない |
JSON_PRETTY_PRINT | 整形(読みやすく) |
JSON_PRESERVE_ZERO_FRACTION | 1.0 を 1 にせず保持 |
JSON_INVALID_UTF8_SUBSTITUTE | 不正なUTF-8を置換(落とさない) |
JSON_THROW_ON_ERROR | 例外で失敗を検出 |
深いネストを扱うときは depth
に気を配りましょう。なぜなら、過剰な入れ子は失敗の原因になりうるからです。
2-2-3. エラー検出と典型トラブル
エラー検出は2通りあります。
// 例外方式(推奨)
try {
$json = json_encode($data, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
// ログ出力・復旧処理
}
// 従来方式
$json = json_encode($data);
if ($json === false) {
$err = json_last_error(); // コード
$msg = json_last_error_msg(); // メッセージ
}
よくある原因と対処は次のとおりです。
- UTF-8 以外の文字コード:JSONはUTF-8前提です。つまり、事前にUTF-8へ統一しましょう。
- リソース型(ファイルポインタ等):そのままでは JSONエンコード できません。だから、文字列などに変換してから渡します。
- 循環参照:標準では扱えません。参照を断つ、ID化するなどの前処理が必要です。
2-3. Python やその他言語でのエンコード方法
Pythonは json.dumps
が基本です。さらに、他言語でも標準/定番の JSONエンコード 手段が整っています。
したがって、プロジェクト言語ごとに“まずこれ”を覚えるのが近道です。
2-3-1. Python(標準ライブラリ)
import json
data = {“id”: 1, “name”: “太郎”, “tags”: [“json”, “encode”]}
# 読みやすさ重視
print(json.dumps(data, ensure_ascii=False, indent=2))
# 軽量出力(余分な空白を削減)
print(json.dumps(data, ensure_ascii=False, separators=(“,”, “:”)))
ensure_ascii=False
:日本語をエスケープしない(人が読む用途で便利)。default=
:**標準で変換できない型(datetime など)**を文字列化する関数を渡す。sort_keys=True
:キーをソート(差分比較しやすい)。
日時や小数、バイナリなどはカスタム変換が必要です。
つまり、データ型の統一ルールをチームで決めておくと、受け手のデコードが安定します。
2-3-2. Go / Java / C# / Ruby / Rust の代表例
言語 | 標準・定番 | 使い方の要点(JSONエンコード視点) |
---|---|---|
Go | encoding/json | json.Marshal(v) で []byte を取得。構造体タグ json:"name,omitempty" でフィールド名や省略制御。エクスポート(大文字始まり)でないと対象外。 |
Java | Jackson / Gson | Jacksonなら ObjectMapper().writeValueAsString(obj) 。日付や数値書式は Module / Serializer で統一。 |
C# | System.Text.Json / Newtonsoft.Json | JsonSerializer.Serialize(obj) 。既定で循環参照は例外。ReferenceHandler で回避可。 |
Ruby | to_json (jsonライブラリ) | require 'json'; obj.to_json 。ハッシュ/配列は素直に変換。文字コードはUTF-8に統一。 |
Rust | serde_json | serde_json::to_string(&value) 。構造体に #[derive(Serialize)] 。型安全で高速。 |
それぞれ微妙に違います。だからこそ、循環参照・非対応型・文字コードの3点を最初に確認しましょう。
2-3-3. 共通のベストプラクティス(どの言語でも有効)
- UTF-8に統一:JSONエンコード はUTF-8が基本。途中で別のエンコーディングが混じると文字化けします。
- 巨大データは分割:一度に巨大なJSONを作らず、ページングやストリーミングを検討。
- 日付・バイナリの表現統一:ISO 8601文字列、Base64など“チームの約束”を決める。
- 循環参照を避ける設計:ID参照に置き換える、または専用オプション/ライブラリで回避。
- 整形は用途で切り替え:人が読むなら整形、通信なら最小化。つまり、目的に応じて
pretty
とcompact
を使い分ける。
付録:クイック比較表(よく使うオプション)
言語 | 基本関数 | 日本語をそのまま | 整形出力 | 循環参照 |
---|---|---|---|---|
JavaScript | JSON.stringify | そのまま(UTF-8前提) | 第3引数 space | 例外(TypeError) |
PHP | json_encode | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | 不可(前処理が必要) |
Python | json.dumps | ensure_ascii=False | indent | 原則不可(前処理が必要) |
Go | json.Marshal | そのまま | MarshalIndent | 構造次第(循環は設計で回避) |
C# | JsonSerializer.Serialize | そのまま | WriteIndented | 既定で例外(設定で回避) |
JSONエンコードのオプションと設定
「JSONエンコード」は、ただ文字列に変えるだけではありません。読みやすさ、サイズ、安全性、そして失敗検知まで、オプションと設定しだいで大きく品質が変わります。
つまり、用途に合わせて最適なフラグや整形方法を選ぶことが、実務の成否を分けます。
ここでは、よく使う設定を体系的に整理し、すぐに適用できる具体策を示します。
3-1. フラグ・オプション(エスケープ、Unicode、スラッシュ除去など)
3-1-1. 目的別に選ぶ基本オプション(整形・日本語・スラッシュ)
まずは「人が読むか」「機械だけが読むか」で切り替えます。
つまり、人が読むなら整形(pretty)、通信や保存でサイズ重視ならコンパクト(minify)が基本方針です。
- JavaScript(JSON.stringify)
- 整形:
JSON.stringify(obj, null, 2)
- フィルタ:
JSON.stringify(obj, ["id","name"], 2)
(許可キーだけ) - 文字の扱い:Unicodeはそのまま出力(UTF-8前提)
- 整形:
- PHP(json_encode)
- 日本語をそのまま:
JSON_UNESCAPED_UNICODE
- スラッシュ除去:
JSON_UNESCAPED_SLASHES
- 整形:
JSON_PRETTY_PRINT
- 日本語をそのまま:
- Python(json.dumps)
- 日本語をそのまま:
ensure_ascii=False
- 整形:
indent=2
- 軽量化:
separators=(",", ":")
- 日本語をそのまま:
実務の初期設定例(読みやすさ優先):
- JavaScript:
JSON.stringify(data, null, 2)
- PHP:
json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)
- Python:
json.dumps(data, ensure_ascii=False, indent=2)
3-1-2. 失敗検知と安全性を高めるオプション
JSONエンコードでは「いつ・なぜ失敗したか」を掴むことが肝心です。したがって、例外やエラーメッセージを活用しましょう。
- PHP
- 例外で検出:
JSON_THROW_ON_ERROR
- 不正UTF-8を置換:
JSON_INVALID_UTF8_SUBSTITUTE
- 例:
- 例外で検出:
try {
$json = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); } catch (JsonException $e) {
// ログやフォールバック
}
- JavaScript
JSON.stringify
は循環参照でTypeError
。try...catch
で補足し、事前に循環を断つ・ID化する。
- Python
- 非対応型(datetime など)は
default=
で文字列化して回避。
- 非対応型(datetime など)は
import json, datetime def enc(o): if isinstance(o, datetime.datetime): return o.isoformat() raise TypeError json.dumps(data, ensure_ascii=False, default=enc)
さらに、HTMLに埋め込むケースでは、</script>
の早期終了や文字参照を避けるため、追加のエスケープを検討します(PHPなら JSON_HEX_TAG / JSON_HEX_AMP / JSON_HEX_APOS / JSON_HEX_QUOT
)。
3-1-3. 出力を最小化・最大可読にするコツ
- 監査やレビュー向け:整形(pretty)で差分が読みやすい
- ネットワーク向け:改行や空白を省いてサイズ削減
- 小数の扱い:PHPの
JSON_PRESERVE_ZERO_FRACTION
で1.0
を保持 - キー順の安定化:Pythonの
sort_keys=True
で差分比較を容易に
3-2. 深さ(depth)やネスト構造の扱い
深い入れ子は可読性・性能・失敗率のすべてを悪化させます。
だからこそ、深さの上限とデータ設計を意識するだけで、JSONエンコードの安定度が一気に上がります。
3-2-1. 深さ上限の考え方(PHPのdepth、他言語の挙動)
言語 | 深さ指定 | 既定の挙動 | 典型的な失敗 |
---|---|---|---|
PHP | 第3引数 depth (既定 512) | 上限超過で失敗 | 深すぎるネストや循環で例外/エラー |
JavaScript | 直接指定なし | 循環で TypeError 、極端な再帰でスタック問題 | 循環参照・巨大オブジェクト |
Python | 指定なし | 再帰制限到達や非対応型で例外 | 深いネスト、型未対応 |
Go / C# / Java | 指定なし(設計で制御) | フィールド/タグ設定次第 | 深い入れ子、循環・参照グラフ |
したがって、PHPでは depth
を意識し、他言語ではそもそも深い構造を作らない設計が重要です。
3-2-2. ネストを浅くする設計パターン
- 正規化・ID参照:子要素をフラットに分離し、親にはID配列だけを持たせる
- ページング:巨大配列はページ分割して送る
- ストリーミング/逐次出力:結果を分割して順次エンコード(NDJSON、チャンク送信など)
- 集約の上限:1レスポンスに含める階層・件数の上限を仕様化
つまり、必要なときに必要な分だけ送るのが鉄則です。
3-2-3. 循環参照・巨大オブジェクトの扱い
- 循環参照:JSONエンコードは木構造が前提。自己参照はIDに差し替えるか、事前にノードをコピーして循環を断つ。
- 巨大オブジェクト:サマリと詳細を分割し、詳細は別APIで取得。
- キャッシュ活用:重複データは一箇所にまとめ、参照(ID)で繋ぐ。
その結果、失敗率が下がり、通信も軽くなります。
3-3. エスケープされる文字・されない文字
JSONのエスケープ仕様を理解しておくと、「なぜこの文字列だけ見た目が違うのか」を説明でき、不要な不具合を防げます。
なぜなら、言語ごとの既定動作とJSONの仕様には微妙な差が残るからです。
3-3-1. JSONで必ずエスケープ対象になるもの
- ダブルクォート
"
(文字列の区切り) - バックスラッシュ
\
(エスケープ記号) - 制御文字(改行
\n
、復帰\r
、タブ\t
、ベル等) - これらは
\"
\\
\n
\t
といった形で表現されます。
また、スラッシュ /
は必須ではないものの、実装によっては \/
と出力されることがあります(PHPは JSON_UNESCAPED_SLASHES
で抑制可能)。
3-3-2. Unicodeと日本語の扱い(エスケープ可否)
- JSON自体はUnicodeを素直に扱えるため、日本語はそのままでも問題ありません。
- ただし一部の実装は既定で
\uXXXX
表記に変換します。- PHP:既定は
\uXXXX
。対策にJSON_UNESCAPED_UNICODE
- Python:既定はASCIIエスケープ。対策に
ensure_ascii=False
- JavaScript:既定でそのまま(UTF-8で扱う前提)
- PHP:既定は
つまり、“人が読む”場面ではエスケープ抑制を選びましょう。
3-3-3. HTML埋め込み時に追加で避けたい文字
JSON文字列を <script>
内に埋め込む場合、タグ終端やエンティティ関連で問題が起きることがあります。したがって、次の対策を検討します。
- 追加で避けたい文字の例:
<
>
&
"
'
、およびスクリプト文脈で問題になりやすい</script>
断片 - PHP:
JSON_HEX_TAG
(<
>
)、JSON_HEX_AMP
(&
)、JSON_HEX_APOS
('
)、JSON_HEX_QUOT
("
)
echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT );
- JavaScript:必要に応じて
JSON.stringify
後に置換で追加サニタイズ
const safe = JSON.stringify(data).replace(/[<>&]/g, c => ({'<':'\\u003C','>':'\\u003E','&':'\\u0026'}[c]) );
このように、使用文脈(通信・保存・HTML埋め込み)で求められる安全レベルが違うため、JSONエンコードのオプションも使い分けが不可欠です。
エラー・失敗時の対処と注意点
「JSONエンコード」は万能ではありません。つまり、入力データの状態や言語ごとの仕様によっては、失敗・例外・想定外の変換が起こります。
したがって、この章では失敗しやすいケースと確実な検出方法、そして循環参照への現実的な対策を、実務で役立つ順に整理します。
4-1. エンコードに失敗するケース(非 UTF-8 文字列、リソース型、循環参照など)
4-1-1. 文字コード不一致・不正なUnicode(非UTF-8や壊れたサロゲート)
JSONは事実上UTF-8前提です。だからこそ、入力文字列がUTF-8でない、あるいは壊れたサロゲートが混在していると、JSONエンコードが失敗・置換・例外のいずれかになります。
- PHP:
JSON_ERROR_UTF8
や例外(JSON_THROW_ON_ERROR
) - Python:
UnicodeEncodeError
相当の例外やASCIIエスケープ(ensure_ascii=True
) - JavaScript:文字列はUTF-16内部表現だが、壊れたサロゲートは不正な出力の原因に
対策の要点
- 入力は必ずUTF-8へ統一(読み込み時に変換)
- 不正シーケンスは検証して置換(例:PHPの
JSON_INVALID_UTF8_SUBSTITUTE
/ Pythonでの正規化)
4-1-2. 非対応のデータ型(リソース・関数・BigInt・特殊数値など)
JSONは「文字列・数値・真偽・null・配列・オブジェクト」に限られます。つまり、それ以外は原則そのままではJSONエンコード不可です。
例 | 症状 | 回避策 |
---|---|---|
PHPのリソース(ファイルポインタ等) | false や例外 | 事前に識別子やパス文字列に変換 |
JavaScriptの undefined / function / Symbol | キー値なら無視、配列なら null | 除去または文字列化 |
JavaScriptの BigInt | 例外 | 文字列へ変換して保持 |
NaN / Infinity(JS, PHP, Python) | JSON仕様外 | null に置換または文字列化ルールを定める |
Pythonの datetime , set , bytes | TypeError | カスタム変換(ISO 8601、リスト化、Base64など) |
4-1-3. 循環参照・過剰ネスト・巨大構造
自己参照を含むグラフ構造は、標準のJSONエンコードでは表現できません。したがって、多くの言語で例外やスタック問題、depth超過が発生します。
- JavaScript:循環で
TypeError
- PHP:
JSON_ERROR_DEPTH
/ 例外、深さ既定は 512 - Python:再帰深度や型未対応で
RecursionError
/TypeError
対策の要点
- 循環を断つ(ID参照化、コピー時に参照除去)
- 深さ・サイズの上限を設ける(depth/件数/文字数)
- ページングや分割レスポンスで巨大データを避ける
4-2. 戻り値が false や例外になる場合のチェック方法
4-2-1. PHP:json_encode
の戻り値と例外の二段構え
PHPは運用での検出パターンを例外方式に寄せるのが安全です。なぜなら、戻り値 false
の見落としを避けられるからです。
<?php
try {
$json = json_encode($data,
JSON_UNESCAPED_UNICODE
| JSON_THROW_ON_ERROR
| JSON_INVALID_UTF8_SUBSTITUTE
);
} catch (JsonException $e) {
// エラー内容をログ(入力の型・サイズ・発生箇所を必ず付与)
}
従来方式であれば、$json === false
を必ず検査し、json_last_error()
と json_last_error_msg()
を記録します。
4-2-2. JavaScript:try...catch
と事前バリデーション
JSON.stringify
は循環参照で例外を投げます。つまり、事前に循環を検出するか、try...catch
で確実にハンドリングします。
let json;
try {
json = JSON.stringify(payload);
} catch (e) {
// 循環やBigInt等の可能性
json = JSON.stringify(normalize(payload)); // 例:BigInt→文字列、循環→ID参照化
}
ログにはキー数・推定サイズ・ルート型を残すと、原因特定が速くなります。
4-2-3. Python:TypeError
/ValueError
の捕捉と default=
Pythonは非対応型で TypeError
。したがって、default=
で変換ルールを定め、例外時は入力の型を含めて記録します。
import json, datetime
def enc(o):
if isinstance(o, datetime.datetime):
return o.isoformat()
raise TypeError(f”Not JSON-serializable: {type(o)}”)
try:
txt = json.dumps(data, ensure_ascii=False, default=enc)
except (TypeError, ValueError) as e:
# 入力サマリやフィールド名も併記してログ
raise
4-2-4. 運用設計:検出→隔離→復旧の流れをテンプレ化
- 検出:例外・戻り値・処理時間アウト(タイムアウト)
- 隔離:問題データをサンプル化して安全領域に保存(PIIはマスク)
- 復旧:代替ルート(文字列化、部分レスキュー、後続再試行)
- 可視化:ダッシュボードで発生率・原因別内訳を監視
4-3. 循環参照・再帰構造のあるオブジェクトへの対応策
4-3-1. 設計で避ける:ID参照化とスキーマ分割
最も堅いのは、木構造に正規化し、参照はIDで表現する方法です。
つまり、実体を一箇所にまとめ、他所からはIDだけで指すようにします。
Before(循環あり)
User { id:1, name:”A”, team: Team#10 }
Team { id:10, name:”X”, members:[User#1, …] } ← 循環
After(ID参照化)
{
“users”: [{ “id”: 1, “name”: “A”, “teamId”: 10 }],
“teams”: [{ “id”: 10, “name”: “X”, “memberIds”: }]
}
この形なら、JSONエンコードは安定し、受け手もデコードしやすくなります。
4-3-2. 事前正規化(言語別テクニック)
- JavaScript:
replacer
で問題キーを除去・文字列化。循環検出は「訪問済み集合」を使う。
function safeStringify(obj) { const seen = new WeakSet(); return JSON.stringify(obj, (k, v) => { if (typeof v === 'bigint') return v.toString(); if (typeof v === 'function' || typeof v === 'symbol') return undefined; if (v && typeof v === 'object') { if (seen.has(v)) return `[Circular ~]`; seen.add(v); } return v; }); }
- PHP:配列化の段階で循環を断つ・型変換。必要に応じて**深さ上限(depth)**を低めに設定し、失敗時はサマリへフォールバック。
- Python:
default=
でカスタムシリアライズ、dataclasses/asdict
などで循環しない投影を作ってからJSONエンコード。
4-3-3. フェイルセーフ:サイズ/深さガードと部分出力
実サービスでは「完璧に直す」のではなく、落とさず返すことが価値になる場面もあります。
したがって、次のガードを用意します。
- サイズ上限:キー数・配列長・文字数で打ち切り、
"...truncated"
を明示 - 深さ上限:一定深さを超えたらIDや要約へ置換
- タイムアウト:エンコード処理が長い場合は中断して代替応答
- データ品質ルール:NaN/Infinityは文字列化、BigIntは文字列などをスキーマに明文化
応用例・ベストプラクティス
実務で価値を生むのは、単なる仕様理解ではなく「どの場面で、どう JSONエンコード を使うか」を体に染み込ませることです。つまり、この章では API 通信の型、巨大データの扱い、そしてセキュリティまでを一気通貫で整理します。
5-1. API リクエスト/レスポンスでの JSON エンコード利用例
JSONエンコードは、クライアントとサーバー間の“共通言語”です。したがって、正しいヘッダー、一貫したスキーマ、失敗時のふるまいを揃えるだけで、障害率は目に見えて下がります。
5-1-1. リクエストの基本形(送る側)
- ヘッダー:
Content-Type: application/json
- ボディ:アプリ内データを JSONエンコード して送る
- 注意:数値文字列・日付・金額などは表現ルールを事前に合意
例(JavaScript/ブラウザ):
fetch('/api/orders', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
orderId: 'A-1001',
amount: '1200.00', // 金額は文字列で固定小数
currency: 'JPY',
createdAt: new Date().toISOString()
})
});
例(PHP):
$payload = [
'userId' => 123,
'tags' => ['json', 'encode']
];
$body = json_encode($payload, JSON_UNESCAPED_UNICODE);
5-1-2. レスポンスの基本形(返す側)
- ヘッダー:
Content-Type: application/json; charset=utf-8
- ボディ:JSONエンコード済みのオブジェクト
- エラー:HTTP ステータス+JSON で機械可読なエラー詳細を返す
例(Node.js/Express):
res.status(200).json({ ok: true, data: result });
例(PHP):
header('Content-Type: application/json; charset=utf-8');
echo json_encode(['ok' => true, 'data' => $result], JSON_UNESCAPED_UNICODE);
5-1-3. スキーマの一貫性(型・必須・任意)
- 必須/任意/型をチームで固定(null許容も明記)
- 日時は ISO 8601、金額は文字列、IDは文字列で統一
- 配列の空/省略の扱いを決める(空配列を返すか、キーを省くか)
5-1-4. バージョニングと後方互換
- レスポンスに
version
、deprecation
を含める - フィールドを削除せず、まずは非推奨化→移行期間→削除
- だから、追加は互換だが削除は破壊的という原則を徹底する
5-2. 大きなデータを扱うときの最適化・パフォーマンス注意点
巨大 JSON は、帯域・メモリ・CPU・待ち時間の“全部盛り”で効いてきます。したがって、構造を痩せさせる工夫と転送の工夫を同時に行います。
5-2-1. 構造の最適化(まずは痩せさせる)
- フィールドの最小化:使わない項目は返さない(selective fields)
- 正規化:重複するサブオブジェクトは ID 参照化して別配列へ
- ページング:
page
/limit
またはcursor
で分割 - 部分レスポンス:
fields=...
で必要な列だけ返す
5-2-2. エンコードと転送の最適化
- コンパクト出力:整形(pretty)は人間が読む場面だけ
- 圧縮:サーバー側で gzip / br を有効化
- キャッシュ:
ETag
/Last-Modified
で再転送を抑制 - 差分配信:変更点のみ返すエンドポイントを設計
5-2-3. ストリーミングと分割(待たせない設計)
- NDJSON(改行区切りJSON):1行1オブジェクトで逐次処理
- チャンク配信:大きい一覧はチャンクで段階的に返す
- ジョブ化:重い集計は非同期ジョブにして、結果をポーリング取得
5-2-4. 実務チューニングの目安
観点 | 目安・方針 |
---|---|
1レスポンスのサイズ | 数百 KB を超えたら要検討(圧縮・分割) |
ネスト深さ | 3〜4 階層を越えたら正規化検討 |
数値・金額 | 浮動小数は避け、文字列または整数+小数点位置 |
文字列化 | 日時は ISO 8601、バイナリは Base64 |
つまり、読みやすさより転送量を優先すべき場面が多い、ということです。
5-3. セキュリティ・脆弱性への配慮(悪意データ、インジェクション、型チェックなど)
JSONエンコードそのものは安全でも、取り扱い方次第で脆弱性につながります。だからこそ、ここでは現場で効く対策だけを簡潔に押さえます。
5-3-1. 入力バリデーション(最優先)
- スキーマ検証:型、必須、列挙値、範囲、長さをチェック
- 過剰データの拒否:フィールド数・配列長・文字数の上限
- サニタイズ:想定外の制御文字・無効Unicodeを除去/置換
- 数値の扱い:金額・IDは文字列で受け、必要なら厳密変換
その結果、後段のロジックやDBが壊れるリスクを大幅に減らせます。
5-3-2. インジェクション対策(NoSQL/SQL/テンプレート)
- SQL/NoSQL:パラメータバインド・正規化・予約語のブロック
- テンプレート:JSONをそのまま HTML に埋め込む際は追加のエスケープ(
<
>
&
'
"
) - ログ注入:ログは JSONエンコード で構造化し、改行や制御文字を抑制
5-3-3. JSON ハイジャック/CSRF などの周辺対策
- 認証必須:機密レスポンスは常に認証下で返す
- メソッド制御:状態変更は
POST/PUT/PATCH/DELETE
、GET
での副作用を避ける - CSRF 対策:トークン・SameSite Cookie
- JSONP を使わない:CORS と適切なヘッダーで代替
5-3-4. デシリアライズ後の取り扱い(安全に“使う”)
- プロトタイプ汚染に注意(オブジェクトマージ時に
__proto__
などを拒否) - 危険な評価:JSON文字列を
eval
しない(パーサを使う) - 権限境界:受け取ったJSONをそのまま権限判定に使わず、サーバー側で再計算
5-3-5. ログ・監査・マスキング
- PII:個人情報はマスクしてから JSONエンコード
- トレーサビリティ:リクエストID・ユーザーID・時刻を付与
- サンプル保存:障害時に再現できる最小限のデータを安全領域へ
5-3-6. 付録:状況別おすすめ設定(クイック表)
用途 | 推奨設定・方針 |
---|---|
人が読む設定/ログ | 整形出力(pretty)、日本語そのまま(各言語のオプション) |
通信・保存 | コンパクト出力、圧縮、キャッシュ、ページング |
HTML 埋め込み | 追加エスケープ(< > & ' " )、スクリプト文脈の分離 |
金額・日時 | 金額は文字列(固定小数)、日時は ISO 8601 |
大規模一覧 | NDJSON/チャンク配信、差分応答、limit/cursor |
失敗耐性 | 例外化、サイズ/深さ上限、フォールバック(部分レスキュー) |
よくある疑問・ Q&A / トラブルシュート
6-1. 「日本語・マルチバイト文字が化ける」「文字化けする」「エスケープされすぎる」への対処法
JSONエンコードで日本語が「???」になったり、\u30bf\u30ed\u30a6
のように読みにくくなったりする問題は、原因がいくつかに分かれます。
つまり、文字コード不一致、ダブルエンコード、必要以上のエスケープ、出力コンテキストの違いなどです。
以下では、症状ごとに切り分け、言語別の対処、そして再発防止策までを順に整理します。
6-1-1. まずは症状を切り分ける(原因特定が最短ルート)
- 文字化け(豆腐や???、異常記号)
文字コードがUTF-8以外、もしくは不正なUnicodeが混在している可能性。 - Unicodeエスケープが見づらい(
\uXXXX
の羅列)
JSONエンコードの設定で人が読む用途に向かない出力になっているだけ。 - スラッシュやHTML記号が過剰にエスケープ(
\/
や<
など)
二重エスケープやHTML埋め込み対策と混同している可能性。 - JSON文字列をページに埋めたときだけ崩れる
HTTPヘッダーのcharsetやテンプレート側のエスケープが原因になりがち。
切り分けの順番
- 入力が本当にUTF-8か(読み込み時に変換済みか)
- JSONエンコード時のオプション設定が適切か
- 出力時のヘッダー・保存時のファイルエンコーディングがUTF-8か
- テンプレートやJS側で二重エスケープしていないか
6-1-2. 言語別の基本対処(最小のオプションで解決)
- PHP(
json_encode
)
人が読む用途では次を基本形にします。
echo json_encode($data, JSON_UNESCAPED_UNICODE // 日本語を \uXXXX にしない | JSON_UNESCAPED_SLASHES // \/ を / のまま );
- 文字化けや失敗が続く場合は、UTF-8へ統一しつつ以下を併用します。
json_encode($data, JSON_UNESCAPED_UNICODE|JSON_INVALID_UTF8_SUBSTITUTE);
- Python(
json.dumps
)
import json print(json.dumps(data, ensure_ascii=False, indent=2))
- つまり、
ensure_ascii=False
が日本語表示の要です。 - JavaScript(
JSON.stringify
)
通常はUTF-8でそのまま出ます。整形して読みやすくするなら
JSON.stringify(data, null, 2);
- なお、ビルドや配信の段階で文字コードが変わっていないかも確認します。
6-1-3. どこで壊れるかを見抜くチェックリスト
- 入力
- 外部ファイルやDBからの読み込み時点でUTF-8か
- CSVやLegacy DBは読み込み時にUTF-8へ変換しているか
- JSONエンコード
- PHPは
JSON_UNESCAPED_UNICODE
を使っているか - Pythonは
ensure_ascii=False
か
- PHPは
- 出力・保存
Content-Type: application/json; charset=utf-8
を返しているか- ファイル保存時のエンコーディングがUTF-8か
- 表示・埋め込み
- テンプレートやフロント側で重ねてエスケープしていないか
<script>
内埋め込みでは追加対策(後述)を検討したか
6-1-4. 典型的な症状と原因の対応表
症状 | よくある原因 | まず試す対処 |
---|---|---|
??? や 豆腐 | 入力がUTF-8でない/不正Unicode混在 | 取り込み時にUTF-8へ変換、PHPは JSON_INVALID_UTF8_SUBSTITUTE |
\u305f\u308d\u3046 の羅列 | エスケープ表示の既定 | PHPは JSON_UNESCAPED_UNICODE 、Pythonは ensure_ascii=False |
\/ が多い | スラッシュのエスケープ既定 | PHPは JSON_UNESCAPED_SLASHES |
HTML上でだけ崩れる | charset未指定、二重エスケープ | ヘッダーにUTF-8、テンプレートのエスケープ重複を見直し |
JSONが無効になる | ダブルエンコードや混入文字 | 生成箇所を一本化、改行や制御文字を除去・正規化 |
6-1-5. 「エスケープされすぎる」を避ける設計
- スラッシュや日本語が過剰にエスケープされる
目的が閲覧なら、見やすさ重視のオプションを有効化します。
PHPはJSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
、Pythonはensure_ascii=False
。 - ダブルエンコードの回避
JSON文字列をもう一度 JSONエンコードしないよう、データと文字列の境界を明確化します。
例えば、テンプレートに渡すのは「オブジェクト」か「最終的なJSON文字列」かを統一します。 - HTMLへの安全な埋め込み
<script>
タグ内にJSONを直接埋め込むなら、<
や&
などを追加でエスケープして早期終了を避けます。
つまり、JSONのエスケープとHTML文脈のエスケープは目的が異なるため、使い分けが必要です。
6-1-6. 実務テンプレ(再利用しやすい最小構成)
- APIレスポンス(日本語を正しく返す)
- ヘッダーはUTF-8を明示
- 本文は**JSONエンコードの“見やすい設定”**で生成
- ログ出力(後から目検したい)
- 整形出力(pretty)+日本語そのまま
- 保存・通信(サイズ最優先)
- 整形や余分な空白は省略し、圧縮はサーバー側で行う
言語別ミニ例
- PHP
header('Content-Type: application/json; charset=utf-8'); echo json_encode($payload, JSON_UNESCAPED_UNICODE);
- Python
print(json.dumps(payload, ensure_ascii=False))
- JavaScript
res.setHeader('Content-Type','application/json; charset=utf-8'); res.end(JSON.stringify(payload));
6-1-7. 再発防止のための運用ルール
- すべての入出力をUTF-8に統一(読み込み時に変換)
- チームでJSONエンコードの既定オプションを決める(日本語はそのまま、整形の有無など)
- テンプレート側のエスケープとJSONエンコードの責務を分離(ダブルエンコード防止)
- 受け渡し前にスキーマ検証とサニタイズを通す(制御文字や不正Unicodeの除去)
- 回帰テストに日本語・絵文字・機種依存文字を含める

IT資格を取りたいけど、何から始めたらいいか分からない方へ
「この講座を使えば、合格に一気に近づけます。」
- 出題傾向に絞ったカリキュラム
- 講師に質問できて、挫折しない
- 学びながら就職サポートも受けられる
独学よりも、確実で早い。
まずは無料で相談してみませんか?