JSON 文字列エスケープとは?
JSON 文字列エスケープとは、文字列内の特殊文字を、JSON パーサーが構造を壊さずに読み取れるエスケープシーケンスに変換する処理です。JSON 仕様(ECMA-404 / RFC 8259)では、文字列値の中に含まれる特定の文字の前にバックスラッシュを付けることが義務付けられています。適切なエスケープが行われていない場合、文字列内のリテラルのダブルクォートや改行は文字列を途中で終端させてしまい、パースエラーを引き起こします。
すべての JSON 文字列はダブルクォートで区切られます。文字列自体にダブルクォート、バックスラッシュ、または制御文字(U+0000 〜 U+001F)が含まれている場合、その文字をエスケープシーケンスで置き換える必要があります。たとえば、リテラルの改行は \n に、タブは \t に、ダブルクォートは \" になります。また、任意の Unicode コードポイントを \uXXXX 形式(XXXX は4桁の16進数値)で表現することもできます。
アンエスケープ(逆の操作)は、バックスラッシュシーケンスを元の文字に戻します。文字列値が二重エスケープされた JSON ペイロードを受信した場合や、JSON ログエントリから UI やターミナルに表示するために生のテキストを取り出す必要がある場合に使用します。ログ集約パイプラインではこの問題が頻繁に発生します。JSON エンコードされたメッセージが別の JSON ドキュメントの文字列値として格納されると、内側の文字列のバックスラッシュがすべて二重になって届くためです。
JSON エスケープツールを使う理由
バックスラッシュを手動で追加・削除するのは面倒でミスが起きやすく、特に複数行のテキスト、ファイルパス、埋め込みコードスニペットを扱う場合はなおさらです。専用のエスケープツールを使えば、手作業では見落としがちなエッジケースも確実に処理できます。
JSON 文字列エスケープの主なユースケース
JSON エスケープシーケンスリファレンス
JSON 仕様では、必須エスケープとして正確に2種類(ダブルクォートとバックスラッシュ)と、よく使われる制御文字向けの6種類の短いエスケープシーケンスが定義されています。その他の制御文字(U+0000 〜 U+001F)はすべて \uXXXX 形式を使用する必要があります。U+FFFF を超える文字(絵文字など)は UTF-16 サロゲートペアで表現できます:\uD83D\uDE00。
| 文字 | 説明 | エスケープ形式 |
|---|---|---|
| " | Double quote | \" |
| \ | Backslash | \\ |
| / | Forward slash | \/ (optional) |
| \n | Newline (LF) | \n |
| \r | Carriage return | \r |
| \t | Tab | \t |
| \b | Backspace | \b |
| \f | Form feed | \f |
| U+0000–U+001F | Control characters | \u0000–\u001F |
| U+0080+ | Non-ASCII (e.g. emoji) | \uXXXX or raw UTF-8 |
JSON エスケープ vs. JSON エンコーディング
文字列のエスケープと JSON ドキュメント全体のエンコードを混同する開発者が多くいます。
コード例
主要な言語にはこの処理の組み込み関数が用意されています。JavaScript、Python、Go、jq での例を示します:
// JSON.stringify escapes a value and wraps it in quotes
JSON.stringify('Line 1\nLine 2') // → '"Line 1\\nLine 2"'
// To get just the inner escaped string (no surrounding quotes):
const escaped = JSON.stringify('She said "hello"').slice(1, -1)
// → 'She said \\"hello\\"'
// Parsing reverses the escaping
JSON.parse('"tabs\\tand\\nnewlines"') // → 'tabs\tand\nnewlines'
// Handling Unicode: emoji in JSON
JSON.stringify('Price: 5\u20ac') // → '"Price: 5\u20ac"' (raw euro sign)import json
# json.dumps escapes and quotes a string
json.dumps('Line 1\nLine 2') # → '"Line 1\\nLine 2"'
# Ensure ASCII: replace non-ASCII with \uXXXX sequences
json.dumps('Caf\u00e9', ensure_ascii=True) # → '"Caf\\u00e9"'
# Keep UTF-8 characters as-is (default in Python 3)
json.dumps('Caf\u00e9', ensure_ascii=False) # → '"Caf\u00e9"'
# Unescape by round-tripping through json.loads
json.loads('"She said \\"hello\\""') # → 'She said "hello"'package main
import (
"encoding/json"
"fmt"
)
func main() {
// json.Marshal escapes a Go string for JSON
raw := "Line 1\nLine 2\tindented"
b, _ := json.Marshal(raw)
fmt.Println(string(b))
// → "Line 1\nLine 2\tindented"
// Unescape with json.Unmarshal
var out string
json.Unmarshal([]byte(`"She said \"hello\""`), &out)
fmt.Println(out)
// → She said "hello"
}# Escape a raw string into a JSON-safe value echo 'Line 1 Line 2 with tab' | jq -Rs '.' # → "Line 1\nLine 2\twith tab\n" # Unescape a JSON string back to raw text echo '"She said \"hello\""' | jq -r '.' # → She said "hello"