ソフトウェア

JSONエンコードとは?基礎概念から文字化け対策まで徹底解説!

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化する/ライブラリを使う
DateISO文字列に自動変換(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_FRACTION1.01 にせず保持
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エンコード視点)
Goencoding/jsonjson.Marshal(v)[]byte を取得。構造体タグ json:"name,omitempty" でフィールド名や省略制御。エクスポート(大文字始まり)でないと対象外。
JavaJackson / GsonJacksonなら ObjectMapper().writeValueAsString(obj)。日付や数値書式は Module / Serializer で統一。
C#System.Text.Json / Newtonsoft.JsonJsonSerializer.Serialize(obj)。既定で循環参照は例外。ReferenceHandler で回避可。
Rubyto_json(jsonライブラリ)require 'json'; obj.to_json。ハッシュ/配列は素直に変換。文字コードはUTF-8に統一。
Rustserde_jsonserde_json::to_string(&value)。構造体に #[derive(Serialize)]。型安全で高速。

それぞれ微妙に違います。だからこそ、循環参照・非対応型・文字コードの3点を最初に確認しましょう。

2-3-3. 共通のベストプラクティス(どの言語でも有効)

  • UTF-8に統一:JSONエンコード はUTF-8が基本。途中で別のエンコーディングが混じると文字化けします。
  • 巨大データは分割:一度に巨大なJSONを作らず、ページングやストリーミングを検討。
  • 日付・バイナリの表現統一:ISO 8601文字列、Base64など“チームの約束”を決める。
  • 循環参照を避ける設計:ID参照に置き換える、または専用オプション/ライブラリで回避。
  • 整形は用途で切り替え:人が読むなら整形、通信なら最小化。つまり、目的に応じて prettycompact を使い分ける。

付録:クイック比較表(よく使うオプション)

言語基本関数日本語をそのまま整形出力循環参照
JavaScriptJSON.stringifyそのまま(UTF-8前提)第3引数 space例外(TypeError)
PHPjson_encodeJSON_UNESCAPED_UNICODEJSON_PRETTY_PRINT不可(前処理が必要)
Pythonjson.dumpsensure_ascii=Falseindent原則不可(前処理が必要)
Gojson.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 は循環参照で TypeErrortry...catch で補足し、事前に循環を断つ・ID化する。
  • Python
    • 非対応型(datetime など)は default= で文字列化して回避。

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_FRACTION1.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で扱う前提)

つまり、“人が読む”場面ではエスケープ抑制を選びましょう。

3-3-3. HTML埋め込み時に追加で避けたい文字

JSON文字列を <script> 内に埋め込む場合、タグ終端やエンティティ関連で問題が起きることがあります。したがって、次の対策を検討します。

  • 追加で避けたい文字の例:< > & " '、およびスクリプト文脈で問題になりやすい </script> 断片
  • PHPJSON_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, bytesTypeErrorカスタム変換(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:TypeErrorValueError の捕捉と 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. 事前正規化(言語別テクニック)

  • JavaScriptreplacer で問題キーを除去・文字列化。循環検出は「訪問済み集合」を使う。

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)**を低めに設定し、失敗時はサマリへフォールバック。
  • Pythondefault= でカスタムシリアライズ、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. バージョニングと後方互換

  • レスポンスに versiondeprecation を含める
  • フィールドを削除せず、まずは非推奨化→移行期間→削除
  • だから、追加は互換だが削除は破壊的という原則を徹底する

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/DELETEGET での副作用を避ける
  • 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記号が過剰にエスケープ(\/&lt; など)
    二重エスケープHTML埋め込み対策と混同している可能性。
  • JSON文字列をページに埋めたときだけ崩れる
    HTTPヘッダーのcharsetテンプレート側のエスケープが原因になりがち。

切り分けの順番

  1. 入力が本当にUTF-8か(読み込み時に変換済みか)
  2. JSONエンコード時のオプション設定が適切か
  3. 出力時のヘッダー・保存時のファイルエンコーディングがUTF-8か
  4. テンプレートや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
  • 出力・保存
    • 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資格を取りたいけど、何から始めたらいいか分からない方へ

「この講座を使えば、合格に一気に近づけます。」

  • 出題傾向に絞ったカリキュラム
  • 講師に質問できて、挫折しない
  • 学びながら就職サポートも受けられる

独学よりも、確実で早い。
まずは無料で相談してみませんか?