RizTech Academy logo
RizTech Academy
Objects and ArraysLesson 7 of 720 min

JSON, parse and stringify

Everything in this module has been objects and arrays living inside one running programme. The moment data has to leave — to a server, to localStorage, to a file — it has to become text. JSON is how.

What JSON is

JavaScript Object Notation: a text format that looks like JavaScript object syntax and is not JavaScript. It is a data format, and every language reads it — the Django backend an intern meets next, an Android app, a payment gateway.

{
  "customer": "Priya",
  "plates": 3,
  "paid": true,
  "items": ["dal", "rice"],
  "address": { "city": "Pune" }
}

The rules are stricter than JavaScript's, and the differences are exactly where people get stuck:

JSON requires JavaScript allows
Double quotes on keys Unquoted keys
Double quotes on strings Single quotes, backticks
No trailing comma Trailing commas
No comments // comments
Only string, number, boolean, null, array, object Functions, undefined, Date, Map

That last row causes the interesting problems.

Out: stringify

const order = { customer: 'Priya', plates: 3, paid: true };

console.log(JSON.stringify(order));
{"customer":"Priya","plates":3,"paid":true}

A string, ready to send. For something readable, pass a third argument:

console.log(JSON.stringify({ a: 1, b: [1, 2] }, null, 2));
{
  "a": 1,
  "b": [
    1,
    2
  ]
}

The 2 is the indent; the null is a filter you will rarely use. Use the indented form for logs and files, the compact one for the network — every newline is bytes over somebody's mobile data.

In: parse

const text = '{"customer":"Priya","plates":3}';
const order = JSON.parse(text);

console.log(order.plates + 1);
4

Back to a real object, with real numbers.

JSON.parse throws on bad input, which matters because bad input is normal — a truncated response, an HTML error page where you expected JSON:

JSON.parse('{plates:3}');
SyntaxError: Expected property name or '}' in JSON at position 1
JSON.parse(undefined);
SyntaxError: "undefined" is not valid JSON

That second one is worth recognising: it means you parsed something that was not there at all — usually a response that failed. Wrap JSON.parse in try/catch whenever the input came from outside your programme. Module 5 covers try/catch properly; module 8 uses it on every response.

What gets lost

This is the part that produces real bugs, because it fails silently.

const data = {
  customer: 'Priya',
  plates: 3,
  note: undefined,
  when: new Date('2026-09-27T10:00:00Z'),
  greet() {
    return 'hi';
  },
};

console.log(JSON.stringify(data));
{"customer":"Priya","plates":3,"when":"2026-09-27T10:00:00.000Z"}

Three things happened, none of them announced:

  • undefined vanished. The key is gone entirely — not null, absent.
  • The method vanished. Functions are not data.
  • The Date became a string. It looks fine, and it is no longer a date.

That last one is the trap. A round trip does not give you back what you put in:

const back = JSON.parse(JSON.stringify({ when: new Date() }));
console.log(typeof back.when);
console.log(back.when.getFullYear);
string
undefined

getFullYear is undefined, so calling it throws back.when.getFullYear is not a function. Dates do not survive JSON. You must convert them back yourself: new Date(back.when). Module 6 returns to this.

Three more surprises worth knowing:

console.log(JSON.stringify([1, undefined, 3]));
console.log(JSON.stringify({ a: NaN, b: Infinity }));
[1,null,3]
{"a":null,"b":null}

In an array, undefined becomes null rather than disappearing — because dropping it would shift every later index. And NaN and Infinity both become null, since JSON has no way to write them. A failed calculation arrives at the server as an innocent-looking null.

And a circular reference is a hard error:

const order = { customer: 'Priya' };
order.self = order;
JSON.stringify(order);
TypeError: Converting circular structure to JSON

Rare in plain data, common the moment you try to stringify something from the DOM — every element refers to its parent, which refers back.

Value going in Comes out as
undefined in an object key removed
undefined in an array null
Function removed
Date ISO string
NaN, Infinity null
Map, Set {}
Circular reference TypeError

Map and Set becoming {} is the row that catches people — no error, no warning, just an empty object where your data was. Convert with Object.fromEntries(map) or [...set] before stringifying.

The deep-copy trick, and why not to use it

You will see this:

const copy = JSON.parse(JSON.stringify(original));

It does produce a genuine deep copy, and before structuredClone existed it was the standard move. But it silently destroys everything in the table above. Use structuredClone(original) instead — it keeps dates, handles circular references, and is built in.

Check your work

JSON needs double quotes on keys and strings, and allows no trailing commas or comments. Single quotes are the commonest reason hand-written JSON fails to parse.

JSON.stringify(order) gives a string; the third argument indents it. Indent for humans, compact for the network.

JSON.parse('{plates:3}') throws SyntaxError because the key is not quoted. JSON.parse(undefined) gives SyntaxError: "undefined" is not valid JSON, which usually means the thing you were parsing never arrived.

Stringifying drops undefined properties and functions, and turns a Date into a string. None of this warns you.

After a round trip, typeof back.when is 'string' and back.when .getFullYear is undefined. Rebuild with new Date(back.when).

JSON.stringify([1, undefined, 3]) is [1,null,3] — in an array, undefined becomes null so the indices still line up.

NaN and Infinity both stringify to null.

A Map stringifies to {} with no error at all.

A circular reference throws TypeError: Converting circular structure to JSON.

JSON.parse(JSON.stringify(x)) deep-copies but destroys dates, Maps and anything else in the table. structuredClone(x) is the modern answer.

save and load:

function load(store, key) {
  const text = store[key];
  if (text === undefined) return null;

  try {
    const data = JSON.parse(text);
    if (typeof data?.when === 'string') data.when = new Date(data.when);
    return data;
  } catch {
    return null;
  }
}

Three separate failures, three answers. A key that was never saved is undefined before parsing ever happens, so check it first rather than letting JSON.parse(undefined) throw. Corrupted text throws, and the catch turns it into null. And the date is rebuilt by hand, because nothing else will do it for you — typeof data?.when === 'string' guards against the stored object having no when at all.

Returning null for both failures is a deliberate simplification. In real code you often want to tell "never saved" apart from "saved but corrupted", because the second one means something is wrong and deserves a log.

Practice

  1. Stringify an order object, then parse it back and do arithmetic on a number from it.
  2. Print the indented and the compact form of the same object side by side.
  3. Break the parser three ways: single quotes, a trailing comma, and an unquoted key. Read each error.
  4. Parse undefined and recognise the message. Then wrap the call so a bad response does not crash your programme.
  5. Run the lossy example. Stringify an object containing undefined, a function and a Date, and account for all three results.
  6. Round-trip a Date and then call .getFullYear() on it. Read the error, then fix it with new Date(...).
  7. Stringify [1, undefined, 3] and { a: NaN } and explain both nulls.
  8. Stringify a Map with entries in it, get {}, and then do it properly with Object.fromEntries.
  9. Build a circular reference and read the TypeError.
  10. Harder. Write save(key, value) and load(key) that round-trip an object containing a Date through a string and back, with the date arriving as a real Date. Then handle load being given a key that was never saved, and a key whose stored text has been corrupted. Both must return something sensible rather than throwing — this is exactly what module 7's storage lesson will ask of you.

That is module four. You can now shape data the way real applications do — objects for records, arrays for lists, map/filter/reduce instead of loops, destructuring to take it apart, ?. and ?? to survive missing fields, Map and Set for the jobs objects do badly, and JSON to send it anywhere.

One idea has run through the whole module and is worth stating on its own: objects and arrays are handled by reference, and copying them is shallow by default. Almost every confusing bug in this module was a version of that.

Next module: classes and errors — building your own types, and failing on purpose instead of by accident.

Stuck on this lesson?

Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.

About the internship