JSON Stringify and Parse: Escaping, Double Encoding and Common Pitfalls
Basics 8 min readUpdated October 12, 2026
"Stringify" is used for two related things: turning data into JSON text, and turning JSON text into a string value that can sit inside another JSON document. The second one is where the confusing backslashes come from. This guide explains both, shows how to undo them, and lists the values that stringify quietly changes.
Two meanings of stringify
The first meaning is serialization: a program has an object in memory and writes it out as JSON text.
const user = { id: 42, name: "Asha" };
const text = JSON.stringify(user); // '{"id":42,"name":"Asha"}'
const back = JSON.parse(text); // { id: 42, name: "Asha" }The second meaning is nesting. The result of the first step is a string. If you put that string inside another JSON document, every quote and backslash in it has to be escaped, because it is now just text:
{
"event": "user.created",
"payload": "{\"id\":42,\"name\":\"Asha\"}"
}You see this shape in webhooks, message queues, log lines, and database columns that store JSON as text. It is valid JSON, but the inner part is not yet usable as data.
Unstringify: get the data back out
To unescape it, parse the outer document and then parse the string you find inside. That is two parses, one for each layer.
const outer = JSON.parse(body); // { event: "user.created", payload: "{\"id\":42,...}" }
const payload = JSON.parse(outer.payload); // { id: 42, name: "Asha" }import json
outer = json.loads(body)
payload = json.loads(outer["payload"])If the whole text you were given is a quoted string, such as "{\"id\":42}", then one parse produces the inner JSON text and a second parse produces the object. Parse exactly as many times as there are layers, and no more. A real string value, such as a name, should stay a string.
Double encoding: how it happens
Double encoding means the data was stringified twice. It typically happens when one component serializes an object and the next one, unaware, serializes the result again.
const once = JSON.stringify({ id: 42 }); // {"id":42}
const twice = JSON.stringify(once); // "{\"id\":42}" <- now a JSON stringSymptoms include a response that starts with a quote character, an API client that gets a string where it expected an object, and growing numbers of backslashes (\\\") if it happens more than once. The fix belongs at the source: serialize once, send the right Content-Type (application/json), and let the HTTP or database library handle the rest. Patching it by parsing repeatedly on the receiving side hides the bug.
What JSON.stringify silently changes
| Value | In an object | In an array | Notes |
|---|---|---|---|
undefined | Property is omitted | Becomes null | Use null if you need the key to appear. |
| Function, Symbol | Property is omitted | Becomes null | Functions are never serialized. |
NaN, Infinity | Becomes null | Becomes null | JSON has no representation for them. |
Date | ISO 8601 string | ISO 8601 string | Uses the date's toJSON() method. Parsing gives back a string, not a Date. |
Map, Set | Becomes {} | Becomes {} | Convert to an array or object first, for example [...set]. |
BigInt | Throws a TypeError | Throws a TypeError | Convert to a string yourself, or use a replacer function. |
| Circular reference | Throws a TypeError | Throws a TypeError | Remove the cycle or use a replacer that skips repeated objects. |
Use the replacer and indent arguments
const data = { id: 12345678901234567890n, secret: "x", when: new Date(0) };
const text = JSON.stringify(
data,
(key, value) => {
if (key === "secret") return undefined; // drop this property
if (typeof value === "bigint") return value.toString(); // keep big integers exact
return value;
},
2 // indent by 2 spaces
);Parsing safely
- Never use `eval` to parse JSON. It executes whatever text it is given.
JSON.parseonly reads data. - Wrap parsing in a try/catch. Invalid input throws a
SyntaxError, and untrusted text will eventually be invalid. - Validate after you parse. Parsing proves the syntax is valid, not that the shape is what you expect. Check the structure with code or a JSON Schema before using the data.
- Watch for large numbers.
JSON.parseturns every number into a double-precision float, so integers above 9,007,199,254,740,991 lose precision. Keep such identifiers as strings. - Use a reviver to restore types. The second argument of
JSON.parsecan convert known date strings back intoDateobjects.
const data = JSON.parse(text, (key, value) =>
key === "createdAt" && typeof value === "string" ? new Date(value) : value
);Putting JSON inside HTML
If you embed JSON in a <script> block in a web page, a value that contains </script> would end the block early and could allow script injection. Escape the less-than sign when you write the JSON into HTML, for example by replacing < with \u003c. Most template engines and frameworks provide a safe helper for this, so use it instead of concatenating strings.
When to avoid JSON-inside-a-string
Storing JSON as text inside another document makes it opaque to validation, searching and diffing, and it invites double-encoding bugs. Where you control both ends, prefer nesting the object directly. In databases, use a native JSON type such as PostgreSQL's jsonb instead of a text column. Use the string form only when the receiving system requires it, for example when a signature must be computed over exact bytes.
Frequently asked questions
What does "unstringify" mean?
It is the informal name for reversing a stringify step: taking JSON text that has been escaped inside a string and turning it back into real data. In code this is just JSON.parse applied to the inner string.
Why do I see backslashes everywhere in my JSON?
Each layer of JSON-in-a-string escapes the quotes of the layer inside it with a backslash. Many backslashes in a row mean it was stringified several times. Parse it the same number of times, then fix the code that encoded it more than once.
Does JSON.stringify keep the key order?
It writes properties in the order JavaScript keeps them: integer-like keys first in ascending order, then other keys in the order they were added. If you need a specific order for comparison or hashing, sort the keys yourself before writing.
Can I stringify a class instance?
Yes, but only its own enumerable properties are written. Add a toJSON() method to control exactly what the serialized form contains.