Класс `JSON` в JavaScript

Класс `JSON` в JavaScript

В этой статье объясняется класс JSON в JavaScript.

Мы объясним класс JSON в JavaScript с практическими примерами.

YouTube Video

Класс JSON в JavaScript

Объект JSON в основном имеет два метода. JSON.stringify() преобразует объект в JSON-строку, а JSON.parse() создает объект из JSON-строки. JSON — это формат обмена данными, который может выражать только некоторые значения JavaScript. Функции, undefined, Symbol и циклические ссылки нельзя напрямую преобразовать в JSON.

JSON.stringify()

Сначала вот базовый пример преобразования объекта в JSON-строку.

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}
  • Этот код — самый простой пример преобразования обычного объекта в JSON-строку. JSON.stringify возвращает строку синхронно.

Второй аргумент JSON.stringify (replacer)

Указав replacer вторым аргументом для JSON.stringify, вы можете гибко управлять правилами преобразования. Вот пример, где используется массив для сохранения только определённых свойств.

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"}
  • В этом примере свойство password исключается для безопасного вывода в лог. Это полезно, если по соображениям безопасности вы хотите удалить конфиденциальную информацию.

Использование replacer как функции (для преобразования и фильтрации значений)

Если нужна большая гибкость, можно передать функцию для обработки каждого ключа и значения. Ниже приведён пример сохранения Date в виде строки ISO.

 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"}
  • В случае функции-replacer передаются ключ и значение, а возвращаемое значение используется в итоговом JSON. this ссылается на родительский объект, что также можно использовать для вложенных преобразований.

Основы использования JSON.parse()

JSON.parse преобразует JSON-строку обратно в объект. Оборачивайте вызов в try/catch, чтобы обрабатывать неверный 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}
  • Если строка некорректна, будет выброшено исключение, поэтому всегда добавляйте обработку ошибок при работе с внешними источниками.

Восстановление данных с помощью reviver (пример восстановления Date)

Используя второй аргумент reviver в JSON.parse, можно выполнить восстановление значений. Вот пример преобразования сохранённой даты-строки обратно в объект Date.

 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 вызывается для каждого ключа, и возвращаемое им значение используется в итоговом объекте. Пожалуйста, выполняйте строгую проверку формата даты в соответствии с вашим приложением.

Форматирование вывода и аргумент space

Чтобы получить отступы для читаемости, передайте число или строку третьим аргументом в 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} */
  • Для JSON, который будут читать люди (например, логи или вывод в конфигурационные файлы), полезно указывать отступы. Однако имейте в виду, что это увеличивает размер данных.

Как обрабатывать циклические ссылки

JSON.stringify выбрасывает TypeError, если есть циклическая ссылка. Обычно используют replacer с набором seen, чтобы избегать циклических ссылок.

 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]"}
  • Используя WeakSet, можно выявлять циклические ссылки и избегать утечек памяти. В случае циклической ссылки будет возвращено "[Circular]". Также можно присвоить идентификатор ссылки вместо "[Circular]".

Пользовательская сериализация с помощью метода toJSON

Если вы определите метод toJSON в объекте, JSON.stringify будет использовать его возвращаемое значение. Это удобно, если вы хотите внедрить правила преобразования для каждого типа.

 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 позволяет определять правила сериализации на уровне объекта, что делает подход более локальным и интуитивным, чем использование replacer.

Ограничения и особенности использования JSON

К значимым примерам, которые нельзя преобразовать в JSON, относятся: undefined, функции, Symbol, BigInt и циклические ссылки. Также учитывайте точность чисел (например, большие целые числа и округление с плавающей запятой по IEEE).

 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}
  • Для обработки BigInt преобразуйте его в строку или определите представление с помощью своего replacer или toJSON.

Безопасность (при работе с ненадёжным JSON)

JSON.parse сам по себе безопасен и считается безопаснее, чем eval, но опасно сразу доверять распарсенному объекту и использовать его напрямую для доступа к свойствам или выполнения запросов в базу данных. Всегда проверяйте и валидируйте, что данные соответствуют ожидаемой схеме.

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}
  • Для валидации входных данных (проверки схемы) надёжно использовать библиотеки, такие как ajv.

Вопросы производительности

Частая сериализация и десериализация больших объектов создаёт нагрузку на процессор и память. При необходимости рассмотрите передачу только изменений, использование бинарных форматов (например, MessagePack) или потоковую обработку (обработка данных по частям). В браузерных средах можно воспользоваться structuredClone (для копирования) или передаваемыми объектами в postMessage.

Короткие советы

Также стоит учитывать следующие моменты:.

  • Для логов избегайте форматирования (pretty-print) и используйте однострочный JSON для уменьшения размера.
  • Объекты, которые часто сериализуются, стоит заранее упростить, например, убрав лишние свойства.
  • Используйте replacer, чтобы минимизировать обход и исключать ненужные свойства.

Практический пример: отправка и приём запросов API

Наконец, представлена последовательность, показывающая безопасное преобразование объекта в JSON для отправки на сервер и восстановление даты из полученного ответа.

 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}
  • В этом примере поле password исключается перед отправкой, а дата восстанавливается с помощью reviver после получения. В реальной работе потребуется добавить обработку ошибок и тайм-аутов.

Резюме

С помощью JSON.stringify() и JSON.parse() можно преобразовывать объекты и строки друг в друга. Вы можете настраивать процесс преобразования с помощью replacer и reviver, а если реализуете toJSON в классе, то сможете управлять сериализацией каждого объекта индивидуально. Циклические ссылки, BigInt, функции и пр. нельзя обработать напрямую — их нужно обрабатывать предварительно.

Всегда проверяйте внешние данные и убедитесь, что конфиденциальная информация не попадает в логи или коммуникации. Если объём данных большой, эффективно избегать форматирования, рассмотреть передачу только изменений или использовать бинарные форматы.

Вы можете следовать этой статье, используя Visual Studio Code на нашем YouTube-канале. Пожалуйста, также посмотрите наш YouTube-канал.

YouTube Video