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\"}"
}
The value of payload is a string that contains JSON

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 string

Symptoms 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

ValueIn an objectIn an arrayNotes
undefinedProperty is omittedBecomes nullUse null if you need the key to appear.
Function, SymbolProperty is omittedBecomes nullFunctions are never serialized.
NaN, InfinityBecomes nullBecomes nullJSON has no representation for them.
DateISO 8601 stringISO 8601 stringUses the date's toJSON() method. Parsing gives back a string, not a Date.
Map, SetBecomes {}Becomes {}Convert to an array or object first, for example [...set].
BigIntThrows a TypeErrorThrows a TypeErrorConvert to a string yourself, or use a replacer function.
Circular referenceThrows a TypeErrorThrows a TypeErrorRemove 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
);
A replacer function can filter, convert or redact values while serializing

Parsing safely

  • Never use `eval` to parse JSON. It executes whatever text it is given. JSON.parse only 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.parse turns 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.parse can convert known date strings back into Date objects.
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.

Related guides

See all 12 guides

© 2026 TrueJson. All rights reserved.