JavaScript has JSON support built in: JSON.parse() turns a JSON string into a value, and JSON.stringify() turns a value into a JSON string. They look simple, but both have optional arguments and edge cases that cause real bugs: lost dates, silently dropped fields and precision errors. This guide covers both functions in depth.
javascriptconst text = '{"name":"Asha","age":30,"tags":["admin"]}'; const user = JSON.parse(text); user.name; // "Asha"
JSON.parse throws a SyntaxError if the input isn't valid JSON, so wrap untrusted input in try/catch:
javascriptfunction safeParse(text) { try { return { ok: true, value: JSON.parse(text) }; } catch (err) { return { ok: false, error: err.message }; } }
The error message tells you where parsing stopped; see Fix "Unexpected token" and other JSON parse errors.
The second argument to JSON.parse is a reviver function, called for every key/value pair from the innermost outward. It's the standard way to turn date strings back into Date objects:
javascriptconst isoDate = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$/; const data = JSON.parse(text, (key, value) => typeof value === 'string' && isoDate.test(value) ? new Date(value) : value );
Return undefined from a reviver to delete a key from the result.
javascriptJSON.stringify({ a: 1, b: [1, 2] }); // '{"a":1,"b":[1,2]}' JSON.stringify({ a: 1, b: [1, 2] }, null, 2); // pretty printed, 2-space indent
The third argument sets the indentation; see How to pretty print JSON.
This table explains most "where did my field go?" bugs:
| Value | In an object | In an array | At the top level |
|---|---|---|---|
undefined | key omitted | null | undefined (not a string) |
| function | key omitted | null | undefined |
Symbol | key omitted | null | undefined |
NaN, Infinity | null | null | "null" |
Date | ISO string (via toJSON) | ISO string | ISO string |
Map, Set | {} (data lost) | {} | "{}" |
BigInt | throws TypeError | throws | throws |
Map and Set serialize to empty objects, so convert them first: Object.fromEntries(map) and [...set].
The second argument to stringify can be an array of keys to keep, or a function that transforms values:
javascript// Keep only these keys JSON.stringify(user, ['id', 'name']); // Redact secrets and serialize BigInt as a string JSON.stringify(data, (key, value) => { if (key === 'password' || key === 'token') return '[REDACTED]'; if (typeof value === 'bigint') return value.toString(); return value; });
For more redaction patterns, see Redact / sanitize JSON.
If an object has a toJSON() method, stringify serializes its return value instead. This is how Date becomes an ISO string, and you can use it in your own classes:
javascriptclass Money { constructor(cents, currency) { this.cents = cents; this.currency = currency; } toJSON() { return { amount: this.cents, currency: this.currency }; } }
stringify throws "Converting circular structure to JSON". Remove the cycle, or use a replacer that tracks objects it has already seen.JSON.stringify on something that's already a JSON string wraps it in quotes and escapes it. If you see "{\"a\":1}", you serialized twice.JSON.parse(JSON.stringify(obj)) loses dates, Maps, undefined and class instances. Use structuredClone(obj) instead.await response.json() parses for you. Calling JSON.parse on the result again throws an error.When parsing fails on a large payload, paste it into the JSON Formatter, which highlights where the syntax breaks. For working with JSON in Python, see Work with JSON in Python.