JSON.rawJSON()

Creates a marker object whose exact JSON text is inserted verbatim during serialization.

Signature
JSON.rawJSON(string)
  • Added in JavaScript ES2025

Parameters

ParameterTypeDescription
$stringstringValid JSON text representing a primitive value — a number, string, boolean, or null. Objects and arrays are rejected, as is any text that is not well-formed JSON.

Return value

ObjectA frozen, null-prototype object with a rawJSON property holding the given text, internally marked so that JSON.stringify() emits that text unchanged.

Errors

Throws a SyntaxError if the string is not valid JSON, or if it represents an object or an array.

Changelog

  • PHP ES2025Introduced together with JSON.isRawJSON() and the reviver's context.source argument as the JSON source-text access proposal. Baseline newly available March 2025.

JSON.rawJSON() solves one specific problem: a JavaScript number cannot hold every JSON number. A 64-bit database id or a monetary value like 1.50 loses its exact text the moment it becomes a double, and no amount of care in JSON.stringify() can bring it back.

Wrapping the digits with JSON.rawJSON() creates a small frozen marker object. JSON.stringify() recognises it and splices the stored text into the output exactly as written, without quotes and without touching it. Pair it with the reviver's context.source and you get a genuinely lossless round trip: read the original digits on the way in, hand the same digits back on the way out.

Rules to know

The argument must be a complete JSON primitive. JSON.rawJSON("[1,2]") and JSON.rawJSON('{"a":1}') both throw, as does anything the JSON grammar rejects, such as a leading +, a bare .5, or a raw newline inside a string. The result is frozen with a null prototype, so it cannot be mutated or accidentally serialized as an ordinary object — but it is still an object, so typeof reports "object" and arithmetic on it will not work.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Echo a 64-bit identifier back to an API without rounding it to the nearest double.
  • Serialize a BigInt as an unquoted JSON number instead of a string.
  • Preserve trailing zeros in a decimal amount such as 1.50 for a payment payload.
  • Keep the original \u escape sequences of a string instead of emitting the character.

Frequently asked questions

Why does JSON.stringify round my large numbers?

JSON numbers are parsed into 64-bit doubles, which cannot represent integers above 2^53 - 1 exactly, so the digits are already lost before serialization starts. Wrap the original text with JSON.rawJSON() so stringify emits it verbatim.

Can I use JSON.rawJSON() to insert a whole object or array?

No — it accepts only JSON primitives, and passing '[1,2]' or '{"a":1}' throws a SyntaxError. Build the surrounding structure normally and use raw JSON markers for the individual leaf values that need exact text.

Which engines support JSON.rawJSON()?

It reached Baseline as newly available in March 2025, so current Chrome, Edge, Firefox and Safari have it, along with recent Node.js. Feature-detect with typeof JSON.rawJSON === "function" or load the core-js polyfill for older targets.

More examples

A lossless round trip

context.source captures the digits before they are rounded, and rawJSON carries them back out unchanged, so the re-serialized text matches the input byte for byte. The last line shows what a plain parse does to the same id: 9007199254740992.

Example · javascript
const incoming = '{"userId":9007199254740993,"name":"Ada"}';

const record = JSON.parse(incoming, (key, value, context) =>
  key === "userId" ? JSON.rawJSON(context.source) : value,
);

console.log(JSON.stringify(record));
console.log(JSON.stringify(record) === incoming.replaceAll(" ", ""));
console.log(JSON.parse(incoming).userId);

Serializing BigInt as a JSON number

The first replacer emits the balance as a bare JSON number; the second, the usual workaround, emits it as a quoted string. Without either, stringify throws a TypeError on the BigInt.

Example · javascript
const account = { balance: 9007199254740993n, currency: "EUR" };

console.log(JSON.stringify(account, (key, value) =>
  typeof value === "bigint" ? JSON.rawJSON(value.toString()) : value,
));

console.log(JSON.stringify(account, (key, value) =>
  typeof value === "bigint" ? value.toString() : value,
));

What it refuses

Every JSON primitive is accepted, including strings, booleans and null. Arrays, objects, and number forms the JSON grammar does not allow — a leading plus, a missing integer digit — all raise SyntaxError at the point the marker is created, not at stringify time.

Example · javascript
for (const text of ['"ok"', "true", "null", "-0.5e10", "[1,2]", '{"a":1}', "+1", ".5"]) {
  try {
    console.log(text, "->", JSON.stringify({ v: JSON.rawJSON(text) }));
  } catch (err) {
    console.log(text, "->", err.name);
  }
}

Discussion

  • Be the first to comment on this lesson.