Skip to content

JSON and data formats

JSON is the format in which servers and browsers exchange data. Whatever an app stores in the browser, such as the favorites, is usually JSON text as well. JSON looks like JavaScript but knows fewer types, and that's exactly where the common mistakes come from.

Learning goals

After this unit you can

  • turn data into text with JSON.stringify and get it back with JSON.parse,
  • say which values JSON doesn't know and what happens to them,
  • keep dates intact through JSON,
  • copy objects including all inner objects with structuredClone.

JSON.stringify turns a value into text, and JSON.parse reads the text back in. The text follows strict rules. Keys and strings are in double quotes, and there are only objects, arrays, strings, numbers, true, false and null.

stringify.mjs
const item = {
id: 2,
name: 'Tent',
deposit: 50,
lentUntil: new Date('2026-11-12T00:00:00Z'),
note: undefined,
}
const text = JSON.stringify(item)
console.log(text)
const back = JSON.parse(text)
console.log(typeof back.lentUntil, back.note)
console.log(JSON.stringify({ tags: ['kitchen', 'raclette'] }, null, 2))

The first line shows what JSON makes of an object with a date. The date becomes a string in ISO 8601 format, and the field note is missing entirely, because JSON doesn't know undefined. After JSON.parse, lentUntil is therefore a string and no longer a Date. With the third argument, here 2, JSON.stringify indents the text for reading.

Value in JavaScriptWhat it becomes in JSON
undefined and functions in an objectthe field is missing
undefined and functions in an arraynull
Datea string such as "2026-11-12T00:00:00.000Z"
NaN and Infinitynull
Map and Setan empty object {}

Because JSON.parse always creates new objects, some programs copy data with JSON.parse(JSON.stringify(original)). That copies nested objects too, but loses everything JSON doesn't know. Since 2022 all major browsers have structuredClone, which also copies dates, Map and Set correctly.

copy.mjs
const original = { name: 'Tent', from: new Date('2026-11-02T00:00:00Z'), parts: ['pegs'] }
const viaJson = JSON.parse(JSON.stringify(original))
const viaClone = structuredClone(original)
viaClone.parts.push('tarp')
console.log(original.parts.length, viaClone.parts.length)
console.log(viaJson.from instanceof Date, viaClone.from instanceof Date)
try {
JSON.parse("{ 'name': 'Zelt' }")
} catch (error) {
console.log(error.name)
}

The copy made with structuredClone has an array parts of its own, so push doesn't change the original. With structuredClone the date stays a Date, while the copy through JSON turns it into text. The last line shows a mistake you will run into often. JSON requires double quotes, and with single ones JSON.parse throws a SyntaxError.

structuredClone can't copy functions and throws an error in that case. For data that comes from a server this doesn't matter, because such data contains no functions.

MistakeWhat helps
After JSON.parse a date is a string, and date.getDay() throws a TypeError.Turn the text back into a date with new Date(...) after reading it.
JSON is written by hand with single quotes or a trailing comma.Create JSON with JSON.stringify, not by hand.
A Set is stored and comes back as {}.Turn it into an array before storing it, [...set].
JSON.parse runs without try, and one broken entry in storage brings the app down.Wrap data you read in try and catch, as in the unit Handling errors.
An AI assistant copies data with JSON.parse(JSON.stringify(...)).Use structuredClone, which keeps dates as well.

Exercise Getting started

Read the JSON text '{"id": 6, "name": "Projector", "deposit": 80, "available": false}' with JSON.parse. Print the name, half the deposit and "free" or "on loan", depending on what available says.

Solution
solution-1.mjs
const text = '{"id": 6, "name": "Projector", "deposit": 80, "available": false}'
const item = JSON.parse(text)
console.log(item.name, item.deposit / 2, item.available ? 'free' : 'on loan')

The output is Projector 40 on loan. After JSON.parse, deposit is a number and available a boolean, so you can calculate and decide with them directly.

Exercise Going further

Turn the reservation { item: 'Tent', from: new Date('2026-11-02T00:00:00Z') } into JSON and back. Make sure from is a Date again afterwards, and print the day of the month.

Solution
solution-2.mjs
const reservation = { item: 'Tent', from: new Date('2026-11-02T00:00:00Z') }
const text = JSON.stringify(reservation)
const back = JSON.parse(text)
back.from = new Date(back.from)
console.log(text)
console.log(back.from.getUTCDate(), back.from instanceof Date)

The JSON text contains the date as a string in ISO 8601 format. new Date(back.from) reads this string as a date again. getUTCDate returns the day in UTC, so the result is 2 everywhere in the world.

Exercise Challenge

A member's favorites are stored in a Set, { pageSize: 20, favorites: new Set([3, 7]) }. What does JSON.stringify make of these settings? Store them so that the numbers of the favorites are kept.

Solution
solution-3.mjs
const settings = { pageSize: 20, favorites: new Set([3, 7]) }
console.log(JSON.stringify(settings))
console.log(JSON.stringify({ ...settings, favorites: [...settings.favorites] }))

A Set has no fields that JSON could see, so it becomes {}, and the favorites are gone. With [...settings.favorites] the Set becomes an array before storing. When reading, new Set(array) turns it back into a Set.

Text and figures are licensed under CC BY-SA 4.0, the code under the MIT License and the course data under CC0 1.0. The figures use the font Kalam under the SIL Open Font License 1.1. The sources are on GitHub.