← Back to all posts Explainer

What a JSON parser changes about your data (and what stays the same)

May 27, 2026·7 min read

JSON looks like a perfectly faithful format: text in, text out. In practice every standard JSON parser quietly transforms a few things on the way through. This post walks through the four cases that matter most, with examples you can paste into any formatter to see for yourself.

The short version

A standard JSON parser preserves the structure of your data, the keys, the string values, and the order of arrays. It does not preserve four specific things:

  1. Duplicate object keys — only the last value for each repeated key survives
  2. Integers larger than about 9 × 1015 — precision is lost
  3. Negative zero — becomes positive zero
  4. The textual form of numbers in scientific notation — re-emitted in plain decimal

These four transformations show up across nearly every modern JSON parser because they come from the underlying floating-point and string-to-number rules rather than from any single library.

1. Duplicate object keys collapse to the last value

The JSON specification (RFC 8259) says object names "should be unique" but does not require parsers to reject duplicates. Most parsers, including the one built into web browsers, silently keep only the last value when keys repeat inside the same object.

Example input:

{"theme": "light", "theme": "dark"}

After parsing and re-stringifying:

{"theme": "dark"}

The first "theme": "light" pair is silently dropped. The output is still valid JSON, but it no longer represents the original document.

Where this shows up

How to spot it

Within any object literal, scan for two keys with the same name at the same nesting level. The detection gets harder as objects nest and grow. A parser that warns on duplicates removes the manual scan.

2. Integer precision loss past 253

Many languages store numbers as 64-bit floating-point values (IEEE‑754 doubles). That format can represent any integer from -(253 − 1) to 253 − 1 (the range from −9,007,199,254,740,991 to +9,007,199,254,740,991) exactly. Past that range, integers are rounded to the nearest representable float.

Example input:

{"id": 9999999999999999999}

After parsing:

{"id": 10000000000000000000}

The number is still numeric, still valid JSON, but it is no longer the same value. There is no warning from a standard parser because the conversion is silent.

Where this shows up

How to spot it

Any integer literal with more than 16 digits is a candidate for precision loss. The safe workaround for systems that need full precision is to serialise such IDs as strings. The string survives the round trip exactly.

Quick analogy

An IEEE‑754 double has a fixed number of significant digits. Putting a 20-digit integer into it is like writing a 20-digit number on a 16-digit display — the rightmost digits get rounded off, even though the total magnitude is preserved.

3. Negative zero becomes positive zero

The JSON spec allows -0 as a valid number literal. Most parsers store it using the same underlying value as 0, and the re-emitted output drops the minus sign.

Example input:

{"adjustment": -0}

After parsing:

{"adjustment": 0}

The distinction between -0 and 0 matters in a few mathematical contexts (for example, division producing infinity with sign), but for most JSON use cases it is invisible. The point worth knowing is that the literal form is not preserved.

4. Scientific notation is re-emitted in plain decimal

The JSON spec allows numbers written in scientific form, such as 1.5e3. After parsing, the in-memory value is just the number itself. When that value is re-stringified, the standard output writer uses the shortest unambiguous representation, which is usually plain decimal.

Example input:

{"distance": 1.5e3}

After parsing:

{"distance": 1500}

The numeric value is preserved exactly. Only the textual form changes. This rarely causes problems, but it can surprise teams that intentionally used scientific notation for readability or to match an upstream system.

Why all four happen

All four transformations come from the same source: the parser converts JSON text into a language-level data structure (numbers, strings, booleans, arrays, objects) and then re-emits text from that structure. Anything that does not fit cleanly into the language's data model — duplicate keys, integers bigger than the float type can hold, signed zero, alternate number forms — is normalised during the conversion.

The original textual form is not stored anywhere by default. Once the in-memory value exists, the path back to text loses the surface details.

How to detect these before they cause a problem

  1. Visual scan. Search for duplicate key names inside each object. Look for integer literals with more than 16 digits. Look for -0 and e+ / e- patterns in the input.
  2. Use a parser that warns. A formatter can flag all four cases automatically by walking the source text before parsing, so the warning sits next to the output.
  3. Use string form for high-precision integers. When a 64-bit ID needs to round-trip exactly, serialise it as a JSON string rather than a JSON number. The string is parsed as-is and re-emitted as-is.

Format JSON with these warnings built in

Free, runs in your browser, flags all four cases automatically.

Open the formatter →

What this post is not

It is not a list of bugs to fix. None of these behaviours are mistakes in any specific parser. They are consequences of the JSON spec leaving certain details to the implementation, combined with the host language's number representation. Knowing they exist is enough to avoid the surprises that follow when a round trip silently changes a value.

Related reading