The `JSON` class in JavaScript

The `JSON` class in JavaScript

This article explains the JSON class in JavaScript.

We will explain the JSON class in JavaScript with practical samples.

YouTube Video

The JSON class in JavaScript

The JSON object mainly has two methods. JSON.stringify() converts an object into a JSON string, and JSON.parse() creates an object from a JSON string. JSON is a data exchange format that can express only some JavaScript values. Functions, undefined, Symbol, and circular references cannot be directly converted to JSON.

JSON.stringify()

First, here is a basic example of converting an object to a JSON string.

1// Convert a JavaScript object to a JSON string.
2const obj = { name: "Alice", age: 30, active: true };
3const jsonString = JSON.stringify(obj);
4console.log(jsonString); // {"name":"Alice","age":30,"active":true}
  • This code is the most basic example of converting a regular object into a JSON string. JSON.stringify returns the string synchronously.

The second argument of JSON.stringify (replacer)

By specifying replacer, the second argument of JSON.stringify, you can finely control the conversion rules. Here is an example using an array to keep only specific properties.

1// Use a replacer array to include only specific properties during stringification.
2const user = { id: 1, name: "Bob", password: "secret", role: "admin" };
3const safeJson = JSON.stringify(user, ["id", "name", "role"]);
4console.log(safeJson); // {"id":1,"name":"Bob","role":"admin"}
  • In this example, password is excluded so that safe log output is possible. This is useful when you want to remove sensitive information for security reasons.

Using replacer as a function (for value conversion and filtering)

If you want more flexibility, you can provide a function to process each key and value. Below is an example of keeping a Date as an ISO string.

 1// Use a replacer function to customize serialization.
 2const data = { name: "Carol", joined: new Date("2020-01-01T12:00:00Z") };
 3const jsonCustom = JSON.stringify(data, (key, value) => {
 4  // Convert Date objects to ISO strings explicitly
 5  if (this && this[key] instanceof Date) {
 6    return this[key].toISOString();
 7  }
 8  return value;
 9});
10console.log(jsonCustom); // {"name":"Carol","joined":"2020-01-01T12:00:00.000Z"}
  • In a function type replacer, the key and value are passed and the returned value is used in the final JSON. this refers to the parent object, so it can also be used for nested conversions.

The basics of JSON.parse()

JSON.parse converts a JSON string back to an object. Wrap it with try/catch so that you can handle invalid JSON.

1// Parse a JSON string into an object and handle parsing errors.
2const jsonText = '{"title":"Example","count":42}';
3try {
4  const parsed = JSON.parse(jsonText);
5  console.log(parsed.title); // Example
6} catch (err) {
7  console.error("Invalid JSON:", err.message);
8}
  • If the string is invalid, an exception will be thrown, so always include exception handling when dealing with external sources.

Custom restoration using reviver (example of restoring Date)

By using the second argument reviver of JSON.parse, you can perform value restoration. Here is an example of converting a stored string date back to a Date object.

 1// Use a reviver to turn ISO date strings back into Date objects.
 2const jsonWithDate = '{"name":"Dana","joined":"2021-06-15T09:00:00.000Z"}';
 3const obj = JSON.parse(jsonWithDate, (key, value) => {
 4  // A simple check for ISO date-like strings
 5  if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$/.test(value)) {
 6    return new Date(value);
 7  }
 8  return value;
 9});
10console.log(obj.joined instanceof Date); // true
  • reviver is called for each key, and the returned value is used in the final object. Please perform strict date format checking according to your application.

Pretty-printing and the space argument

To output with indentation for readability, pass a number or string as the third argument to JSON.stringify.

1// Pretty-print an object with 2-space indentation for readability.
2const config = { host: "localhost", port: 3000, debug: true };
3const pretty = JSON.stringify(config, null, 2);
4console.log(pretty);
5/* {
6  "host": "localhost",
7  "port": 3000,
8  "debug": true
9} */
  • For JSON intended to be read by people, such as logs or configuration file outputs, it's helpful to specify indentation. However, be aware that this increases the size.

How to handle circular references

JSON.stringify throws a TypeError if there is a circular reference. A common solution is to create a replacer using a seen set to avoid circular references.

 1// Safely stringify objects that may contain circular references.
 2function safeStringify(value) {
 3  const seen = new WeakSet();
 4  return JSON.stringify(value, (key, val) => {
 5    if (val && typeof val === "object") {
 6      if (seen.has(val)) return "[Circular]";
 7      seen.add(val);
 8    }
 9    return val;
10  });
11}
12
13const a = { name: "A" };
14a.self = a;
15console.log(safeStringify(a)); // {"name":"A","self":"[Circular]"}
  • By using a WeakSet, you can detect circular references while avoiding memory leaks. In the case of a circular reference, it returns "[Circular]". It is also possible to assign a reference ID instead of "[Circular]".

Custom serialization with the toJSON method

If you define a toJSON method on an object, JSON.stringify uses its return value. This is useful when you want to embed conversion rules for each type.

 1// Define toJSON on a class to customize its JSON representation.
 2class Point {
 3  constructor(x, y) {
 4    this.x = x;
 5    this.y = y;
 6  }
 7  toJSON() {
 8    // This will be used by JSON.stringify
 9    return { x: this.x, y: this.y, type: "Point" };
10  }
11}
12
13const p = new Point(10, 20);
14console.log(JSON.stringify(p)); // {"x":10,"y":20,"type":"Point"}
  • toJSON allows you to define serialization rules at the object level, making it more localized and intuitive than using replacer.

Limitations and caveats of JSON

Notable examples that cannot be converted to JSON are undefined, functions, Symbol, BigInt, and circular references. Be aware of numeric precision as well (such as large integers and IEEE floating-point rounding).

 1// Demonstrate values that can't be represented in JSON.
 2const sample = {
 3  a: undefined,
 4  b: function () {},
 5  c: Symbol("s"),
 6  d: 9007199254740993n // BigInt (note: JSON.stringify will throw on BigInt)
 7};
 8// JSON.stringify will throw for BigInt and will drop undefined, functions, symbols in objects.
 9try {
10  console.log(JSON.stringify(sample));
11} catch (err) {
12  console.error("Error during stringify:", err.message);
13}
  • When handling BigInt, convert it to a string or define its representation using a custom replacer or toJSON.

Security (when handling untrusted JSON)

JSON.parse itself is safe and considered safer than eval, but it is dangerous to trust the parsed object and use it directly for property access or database queries. Always validate and check that the data conforms to the expected schema.

1// Parse external JSON and validate expected properties before use.
2const external = '{"username":"eve","role":"user"}';
3const parsed = JSON.parse(external);
4if (typeof parsed.username === "string" && ["user","admin"].includes(parsed.role)) {
5  console.log("Safe to use parsed.username and parsed.role.");
6} else {
7  throw new Error("Invalid payload");
8}
  • For input validation (schema validation), using libraries such as ajv is robust.

Performance considerations

Frequently serializing and deserializing large objects puts a load on the CPU and memory. If necessary, you can consider sending only the differences, using binary formats (like MessagePack), or streaming (processing data sequentially). In browser environments, there are cases where you can make use of structuredClone (for copying) or transferable objects in postMessage.

Specific advice (short)

You can also consider the following points:.

  • For logs, avoid pretty-print and use single-line JSON to reduce size.
  • Objects that are serialized frequently should be made lightweight beforehand, for example by removing unnecessary properties.
  • Use replacer to minimize traversal and exclude unnecessary properties.

Practical example: API request send/receive flow

Finally, here is a sequence showing how to safely convert an object to JSON for sending to a server and restore a date from the received response.

 1// Prepare payload, stringify safely, send via fetch, and revive dates on response.
 2async function sendUserUpdate(url, user) {
 3  // Remove sensitive info before sending
 4  const payload = JSON.stringify(user, (k, v) => (k === "password" ? undefined : v));
 5  const res = await fetch(url, {
 6    method: "POST",
 7    headers: { "Content-Type": "application/json" },
 8    body: payload
 9  });
10
11  const text = await res.text();
12  // Reviver: convert ISO date strings back to Date
13  const data = JSON.parse(text, (key, value) => {
14    if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$/.test(value)) {
15      return new Date(value);
16    }
17    return value;
18  });
19  return data;
20}
  • In this example, password is excluded before sending, and date is restored by reviver after receiving. In actual operation, you will need to add error handling and timeout processing.

Summary

Using JSON.stringify() and JSON.parse(), you can convert between objects and strings. You can customize the conversion process using replacer and reviver, and if you implement toJSON in a class, you can also control each object individually. Circular references, BigInt, functions, etc. cannot be handled as-is, so you need to process them beforehand.

Always validate external input, and make sure sensitive information is not included in logs or communications. If the data size is large, it is efficient to avoid pretty-printing, consider differential transmission, or use binary formats.

You can follow along with the above article using Visual Studio Code on our YouTube channel. Please also check out the YouTube channel.

YouTube Video