JSON: parse and stringify

Turn JSON text into JavaScript values and back, handle broken input without crashing, and know what JSON cannot carry.

  • Beginner
  • 6 min read
  • Updated 17 Oct 2026

What you will learn

  • Parse JSON safely
  • Serialise data with JSON.stringify
  • List the values JSON cannot represent
  • Use a reviver to restore dates

Before you start

JSON (JavaScript Object Notation) is a text format for data. Servers send it, configuration files use it, and browsers store it. It looks almost like JavaScript object literals, but it is a string, so you must convert it before you can use it. This lesson covers both directions and introduces the data file for the course dashboard you will build from here on.

JSON is text

This is JSON:

JSON
{
  "title": "JavaScript Complete Course",
  "lessons": 54,
  "free": true,
  "tags": ["javascript", "web"],
  "author": null
}

It is stricter than a JavaScript object literal:

  • Keys must be in double quotes.
  • Strings must use double quotes. Single quotes are not allowed.
  • No trailing commas, no comments.
  • Values can only be strings, numbers, true, false, null, arrays and objects.

JSON.parse: text to values

JSON.parse takes a JSON string and returns the JavaScript value it describes:

JavaScript
const text = '{"title":"JavaScript Complete Course","lessons":54,"tags":["javascript","web"]}';

const course = JSON.parse(text);
console.log(course.lessons);  // → 54
console.log(course.tags[1]);  // → web
console.log(typeof text, typeof course); // → string object

The top-level value does not have to be an object. JSON.parse('[1, 2]'), JSON.parse('"hi"') and JSON.parse('42') are all valid.

Parsing safely

Invalid JSON throws a SyntaxError. Text from outside your program (a server, a file, localStorage, a user) can be broken or empty, so wrap the parse in try...catch:

JavaScript
function parseJson(text, fallback) {
  try {
    return JSON.parse(text);
  } catch (error) {
    console.warn('Could not parse JSON:', error.message);
    return fallback;
  }
}

console.log(parseJson('{"ok":true}', {}));  // → { ok: true }
console.log(parseJson("{'ok':true}", {}));  // logs a warning, then → {}
console.log(parseJson('', []));             // logs a warning, then → []

The exact warning text differs between engines; the error type is always SyntaxError. Lesson 35 covers error handling in depth.

Parsing successfully does not mean the data has the shape you expect. A server might send {"courses": null}. Check the parts you rely on, for example with Array.isArray(data) or the optional chaining from lesson 25.

Never use eval to read JSON. JSON.parse only ever builds data. eval runs whatever code is in the string.

JSON.stringify: values to text

JSON.stringify does the reverse:

JavaScript
const settings = { theme: 'dark', fontSize: 18, motion: false };

const json = JSON.stringify(settings);
console.log(json); // → {"theme":"dark","fontSize":18,"motion":false}

The third argument adds indentation, which makes output readable for logs and files:

JavaScript
console.log(JSON.stringify(settings, null, 2));
// → {
//     "theme": "dark",
//     "fontSize": 18,
//     "motion": false
//   }

The second argument is a replacer. Pass an array of keys to include only those properties:

JavaScript
const user = { id: 3, name: 'Tom', password: 'secret' };
console.log(JSON.stringify(user, ['id', 'name'])); // → {"id":3,"name":"Tom"}

A common use of the pair is saving data in the browser, where localStorage only stores strings:

JavaScript
localStorage.setItem('settings', JSON.stringify(settings));
const restored = parseJson(localStorage.getItem('settings'), {});

What JSON cannot represent

JSON has fewer types than JavaScript. stringify does not throw for most unsupported values. It quietly changes or drops them:

JavaScript valueIn an objectIn an arrayOn its own
undefinedProperty droppedBecomes nullReturns undefined
FunctionProperty droppedBecomes nullReturns undefined
NaN, InfinityBecomes nullBecomes null'null'
DateISO stringISO stringISO string
Map, Set{}{}'{}'
BigIntThrows TypeErrorThrows TypeErrorThrows TypeError
Circular referenceThrows TypeErrorThrows TypeErrorThrows TypeError
JavaScript
const data = {
  name: 'Quiz',
  score: NaN,
  helper() {},
  missing: undefined,
  created: new Date('2026-03-01T09:00:00Z'),
};

console.log(JSON.stringify(data));
// → {"name":"Quiz","score":null,"created":"2026-03-01T09:00:00.000Z"}

Round-tripping through JSON therefore loses information. A Date comes back as a string, and a Map as an empty object. Convert a Map with Object.fromEntries(map) before saving, and rebuild it afterwards.

Restoring dates with a reviver

JSON.parse takes an optional second argument, a reviver function. It is called for every key and value, and whatever it returns is used instead:

JavaScript
const saved = '{"title":"Closures","updated":"2026-03-01T09:00:00.000Z"}';

const lesson = JSON.parse(saved, (key, value) =>
  key === 'updated' ? new Date(value) : value
);

console.log(lesson.updated instanceof Date); // → true
console.log(lesson.updated.getUTCFullYear()); // → 2026

Always return value for keys you do not change, or they disappear.

Custom output with toJSON

If an object has a toJSON method, stringify serialises whatever that method returns. That is how Date produces its ISO string, and you can use it in your own classes.

The course dashboard data

From this lesson on, the course's larger project is a course dashboard: a page that loads a list of courses, then filters, sorts, searches and caches it. Create a dashboard folder with this structure. You will fill in index.html and app.js over the next few lessons:

Plain text
dashboard/
  index.html
  app.js
  data/
    courses.json

The data lives in data/courses.json. Each entry has the same six fields: id, title, topic, level, lessons and updated. Save this as the file's contents:

JSON
[
  { "id": 1, "title": "JavaScript Basics", "topic": "JavaScript", "level": "Beginner", "lessons": 12, "updated": "2026-09-14" },
  { "id": 2, "title": "CSS Grid in Practice", "topic": "CSS", "level": "Intermediate", "lessons": 8, "updated": "2026-08-02" },
  { "id": 3, "title": "Accessible Forms", "topic": "Accessibility", "level": "Intermediate", "lessons": 6, "updated": "2026-10-01" },
  { "id": 4, "title": "Responsive Layouts", "topic": "CSS", "level": "Beginner", "lessons": 10, "updated": "2026-07-21" },
  { "id": 5, "title": "Async JavaScript", "topic": "JavaScript", "level": "Advanced", "lessons": 9, "updated": "2026-09-30" },
  { "id": 6, "title": "HTML Forms from Scratch", "topic": "HTML", "level": "Beginner", "lessons": 7, "updated": "2026-06-11" }
]

A few choices are worth noticing. id and lessons are numbers, not strings, so you can compare and sort them directly. updated is a date string in YYYY-MM-DD form, because JSON has no date type; lesson 27 turns it into a readable date. level uses three fixed values, Beginner, Intermediate and Advanced, which later lessons filter on.

Until lesson 33 shows how to load the file with fetch(), you can practise on the same data by pasting part of it into the console as a string:

JavaScript
const coursesJson = '[{"id":1,"title":"JavaScript Basics","topic":"JavaScript","level":"Beginner","lessons":12,"updated":"2026-09-14"},{"id":2,"title":"CSS Grid in Practice","topic":"CSS","level":"Intermediate","lessons":8,"updated":"2026-08-02"}]';

const courses = parseJson(coursesJson, []);
const totalLessons = courses.reduce((sum, course) => sum + course.lessons, 0);
console.log(`${courses.length} courses, ${totalLessons} lessons`);
// → 2 courses, 20 lessons

Summary

  • JSON is text with strict rules: double quotes, no comments, no trailing commas.
  • JSON.parse turns text into values; wrap it in try...catch for any outside input.
  • JSON.stringify turns values into text; use the third argument for indentation.
  • undefined, functions, NaN, Map and Set are dropped or changed; BigInt and circular references throw.
  • Restore special types with a reviver, and keep dashboard data in data/courses.json with six fixed fields.

Further reading