Escape JSON as a String Literal (and Decode Double-Encoded JSON)
Stringifying JSON wraps it in quotes and escapes the inner quotes and special characters, producing a JSON string literal you can safely embed elsewhere.
What Stringifying to a Literal Means
Passing a JSON string to JSON.stringify wraps it in double quotes and escapes the inner quotes, backslashes, and control characters — producing a single string value that is safe to embed inside another JSON document. The JSON Stringify tool handles arbitrary depths in one click.
const inner = '{"name":"Ada","note":"She said \"hi\""}';
const wrapped = JSON.stringify(inner);
// wrapped is: "{\"name\":\"Ada\",\"note\":\"She said \\\"hi\\\"\"}"
The outer document now contains one string value, not a nested object — the receiver has to JSON.parse it once to recover the inner JSON text.
Why You Need It
Three common scenarios force this round trip:
- Embedding JSON inside another JSON field — a webhook
payloadfield that wraps the real body, a job-queue envelope around the task data. - Storing JSON in a string database column (
TEXT/VARCHAR) — older schemas before Postgresjsonband MySQLJSONtypes existed. - Passing JSON as an environment variable or URL parameter — neither can hold raw newlines or unescaped quotes safely.
Escaping Rules
Per RFC 8259 §7, six characters and the general Unicode escape need backslash treatment inside a JSON string:
| Character | JSON escape |
|---|---|
" (double quote) |
\" |
\ (backslash) |
\\ |
/ (solidus) |
\/ (optional — / is also legal unescaped) |
\b (backspace, U+0008) |
\b |
\f (form feed, U+000C) |
\f |
\n (newline, U+000A) |
\n |
\r (carriage return, U+000D) |
\r |
\t (tab, U+0009) |
\t |
| Any other control char (U+0000–U+001F) | \uXXXX |
The result contains no raw line breaks — a stringified JSON value is always a single line. That property is what makes it safe in log lines, env vars, and URL parameters.
Decoding Double-Encoded JSON
If you receive a string that starts with {\" or [\" and contains escaped-quote sequences, it was stringified once too many times. Parse it once to recover the inner JSON text, then parse again to get the actual value:
const raw = '"{\\"name\\":\\"Ada\\"}"'; // double-encoded
const innerText = JSON.parse(raw); // "{\"name\":\"Ada\"}" — still a string!
const value = JSON.parse(innerText); // { name: 'Ada' } — finally the object
If the first parse returns a string instead of an object/array, you're not done — repeat until the result type is what you expected.
Where Double-Encoding Shows Up
The recurring offenders, in rough order of frequency:
- Logging frameworks that serialize context objects with
JSON.stringifybefore joining the line — and a downstream log pipeline that does it again. - Webhook payloads that wrap a JSON body inside a
bodyfield (Stripe, GitHub, Slack signatures all do this in different ways). - Message queues that send JSON-as-string envelopes (SQS messages, Kafka headers).
- Database columns typed as
TEXT/VARCHARwhere someone storedJSON.stringify(obj)instead of using aJSON/jsonbcolumn. - Form fields carrying JSON that got URL-encoded, decoded into a string, then handed to another JSON parser without unwrapping.
The detection rule: look for a value that starts with "{ or "[ (with the leading quote escaped) and contains an even number of backslashes in front of the inner quotes. That's a stringified JSON string.
Working with Embedded JSON in Logs
Structured loggers often stringify context objects to keep each log line a single value — that's the right thing to do for log infrastructure. To inspect a captured line:
- Pull the line out of the log viewer.
- Unescape with
JSON.parse(line)to recover the inner JSON text. - Format with
JSON.stringify(value, null, 2)(or paste into the JSON Stringify tool) to pretty-print.
Avoid grep-ing a multi-line JSON object — JSON stringified to one line is dramatically easier to search.
Tools for the Round-Trip
Three quick options, no install needed:
// Browser DevTools / Node REPL
const value = JSON.parse(line);
console.log(JSON.stringify(value, null, 2));
# Shell — Node CLI
node -e 'console.log(JSON.stringify(JSON.parse(process.argv[1]), null, 2))' "$LINE"
# Shell — jq (if line is on stdin)
echo "$LINE" | jq -r 'fromjson | tojson | fromjson'
The JSON Stringify tool handles arbitrarily deep nesting (triple-encoded, quadruple-encoded) in one click — useful when you can't tell up-front how many JSON.parse calls you need.
See also
This guide pairs with the LLM JSON repair guide (LLM responses sometimes arrive double-encoded when the model wraps its answer in a string literal) and the JSON Stringify tool for the round-trip work.
Sources
- RFC 8259 §7 — the JSON string grammar, including the full escape table
- MDN —
JSON.stringify(the JS reference for the escape behavior) - MDN —
JSON.parse(the reverse direction)
Last reviewed June 2026.
JSON repair guides
Topic hubs
- JSON Parse Errors: Read the Message, Jump to the Fix
- Fix Invalid JSON: From 'What's Wrong' to a Clean File
- JSON Formatter, Validator, Viewer: Pick the Right Tool
- Repair LLM JSON Output: Handling Almost-JSON from AI
- Privacy: JSON Tools That Don't Leave Your Browser
- JSON Interop: YAML, CSV, XML, JWT, Schema
Specific guides
- How to Decode Base64 Strings (and JWT Payloads)
- URL Encoding: Percent-Encode Query Parameters and Paths
- Convert YAML to JSON (and Avoid Indentation Errors)
- Convert JSON to CSV: Flatten an Array of Objects
- Convert JSON to XML: Root Elements, Attributes, and Arrays
- Fix Trailing Comma in JSON
- Fix Single Quotes in JSON
- Fix Unquoted Keys in JSON
- Repair LLM JSON Output
- Fix JSON Parse Error: Expected Property Name
- JSON vs JS Object Literal: The Key Differences
- Validate JSON Before API Requests
- JSON Formatter vs JSON Repair
- Fix JSON Unexpected Token Errors
- JSON to JavaScript Object Converter