Пошук уроків, статей та іншого контенту
Перетворюватимете об’єкти на JSON і назад, враховуючи обмеження серіалізації та втрату окремих типів даних.
Серіалізація — це перетворення структури даних у формат, який можна зберегти або передати:
у файл;
через HTTP-запит;
у localStorage;
між різними програмами або мовами програмування.
JSON — текстовий формат обміну даними, заснований на об’єктах і масивах JavaScript. У JSON підтримуються такі значення:
об’єкти;
масиви;
рядки;
числа;
логічні значення true і false;
null.
У JavaScript для роботи з JSON використовують два основні методи:
JSON.stringify() — серіалізація, перетворення JavaScript-значення на JSON-рядок;
JSON.parse() — десеріалізація, перетворення JSON-рядка назад на JavaScript-значення.
const user = {
id: 42,
name: "Олена",
isActive: true,
roles: ["editor", "author"],
profile: {
city: "Львів"
}
};
const json = JSON.stringify(user);
console.log(json);
// {"id":42,"name":"Олена","isActive":true,"roles":["editor","author"],"profile":{"city":"Львів"}}
console.log(typeof json);
// stringРезультатом JSON.stringify() завжди є рядок або undefined.
JSON-рядок можна передати мережею або зберегти у файл:
const settings = {
theme: "dark",
language: "uk"
};
const serializedSettings = JSON.stringify(settings);
localStorage.setItem("settings", serializedSettings);localStorage зберігає лише рядки, тому серіалізація є необхідною для збереження об’єктів.
const json = '{"id":42,"name":"Олена","isActive":true}';
const user = JSON.parse(json);
console.log(user.name);
// Олена
console.log(typeof user);
// objectПісля JSON.parse() ми отримуємо звичайне JavaScript-значення, з яким можна працювати як із будь-яким об’єктом.
const savedSettings = localStorage.getItem("settings");
if (savedSettings !== null) {
const settings = JSON.parse(savedSettings);
console.log(settings.theme);
}Якщо рядок не є коректним JSON, JSON.parse() викине помилку SyntaxError. Дані, отримані з мережі, файлів або сховища, варто обробляти через try...catch.
const input = '{"name":"Олена",}';
try {
const value = JSON.parse(input);
console.log(value);
} catch (error) {
console.error("Не вдалося прочитати JSON:", error.message);
}У JSON не можна залишати кому після останньої властивості:
{"name":"Олена"}Неправильний варіант:
{"name":"Олена",}За замовчуванням JSON.stringify() створює компактний рядок без зайвих пробілів. Третій аргумент дозволяє додати відступи.
const product = {
id: 7,
name: "Ноутбук",
price: 42000
};
const formattedJson = JSON.stringify(product, null, 2);
console.log(formattedJson);Результат:
{
"id": 7,
"name": "Ноутбук",
"price": 42000
}Третій аргумент може бути числом відступів або рядком:
const json = JSON.stringify({ name: "Олена" }, null, "\t");
console.log(json);Форматований JSON зручний для читання людиною, але для передавання даних зазвичай використовують компактний варіант.
Другий аргумент JSON.stringify() називається replacer. Він може бути масивом імен властивостей.
const user = {
id: 42,
name: "Олена",
email: "olena@example.com",
password: "secret-password"
};
const publicUserJson = JSON.stringify(user, ["id", "name", "email"]);
console.log(publicUserJson);
// {"id":42,"name":"Олена","email":"olena@example.com"}Цей підхід дозволяє явно вказати, які властивості потрібно серіалізувати.
replacer також може бути функцією. Вона викликається для кожної властивості та може змінити або виключити значення. Щоб виключити властивість, потрібно повернути undefined.
const account = {
username: "olena",
password: "secret-password",
token: "temporary-token",
active: true
};
const safeJson = JSON.stringify(account, (key, value) => {
if (key === "password" || key === "token") {
return undefined;
}
return value;
});
console.log(safeJson);
// {"username":"olena","active":true}Не слід покладатися на replacer як на єдиний механізм захисту секретних даних. Краще взагалі не включати паролі, токени та інші секрети в об’єкти, призначені для серіалізації.
Другий аргумент JSON.parse() називається reviver. Це функція, яка викликається для кожної властивості вже розібраного об’єкта.
Вона може:
перетворити значення;
нормалізувати дані;
видалити властивість, повернувши undefined.
Наприклад, JSON не має окремого типу для дат. Дату можна зберегти як рядок, а під час парсингу перетворити її назад на об’єкт Date.
const json = '{"title":"Зустріч","createdAt":"2026-07-25T10:30:00.000Z"}';
const event = JSON.parse(json, (key, value) => {
if (key === "createdAt") {
return new Date(value);
}
return value;
});
console.log(event.createdAt instanceof Date);
// true
console.log(event.createdAt.getUTCFullYear());
// 2026Для надійнішого розпізнавання дат можна перевіряти формат рядка, а не перетворювати кожен рядок із певною назвою властивості.
JavaScript має більше типів даних, ніж JSON. Під час серіалізації частина значень змінюється або втрачається.
undefined, функції та символиВластивості зі значеннями undefined, функціями та символами не потрапляють до JSON-об’єкта.
const object = {
name: "Олена",
missing: undefined,
greet() {
console.log("Привіт");
},
[Symbol("id")]: 42
};
console.log(JSON.stringify(object));
// {"name":"Олена"}У масивах такі значення перетворюються на null:
const values = [
undefined,
function () {},
Symbol("value"),
"текст"
];
console.log(JSON.stringify(values));
// [null,null,null,"текст"]NaN, Infinity та -InfinityJSON не має окремих значень для NaN та нескінченностей. Під час серіалізації вони перетворюються на null.
const numbers = {
notANumber: NaN,
positiveInfinity: Infinity,
negativeInfinity: -Infinity
};
console.log(JSON.stringify(numbers));
// {"notANumber":null,"positiveInfinity":null,"negativeInfinity":null}Якщо важливо зберегти ці значення, потрібно заздалегідь перетворити їх на власне представлення, наприклад рядки, а потім відновити через reviver.
DateОб’єкт Date серіалізується як ISO-рядок.
const data = {
createdAt: new Date("2026-07-25T10:30:00.000Z")
};
const json = JSON.stringify(data);
console.log(json);
// {"createdAt":"2026-07-25T10:30:00.000Z"}Це відбувається тому, що Date має метод toJSON(). Після JSON.parse() значення буде рядком, а не об’єктом Date:
const restored = JSON.parse(json);
console.log(typeof restored.createdAt);
// string
console.log(restored.createdAt instanceof Date);
// falseЯкщо потрібен об’єкт Date, його треба відновити вручну за допомогою reviver.
BigIntСпроба серіалізувати BigInt стандартним способом призводить до помилки.
const data = {
largeNumber: 9007199254740993n
};
try {
JSON.stringify(data);
} catch (error) {
console.error(error.message);
// Do not know how to serialize a BigInt
}Для BigInt можна заздалегідь використати рядкове представлення:
const data = {
largeNumber: 9007199254740993n
};
const json = JSON.stringify(data, (key, value) => {
if (typeof value === "bigint") {
return `${value}n`;
}
return value;
});
console.log(json);
// {"largeNumber":"9007199254740993n"}Під час відновлення такий рядок можна перетворити назад:
const restored = JSON.parse(json, (key, value) => {
if (typeof value === "string" && /^\d+n$/.test(value)) {
return BigInt(value.slice(0, -1));
}
return value;
});
console.log(restored.largeNumber);
// 9007199254740993nНа практиці для цього краще використовувати чіткий формат даних, щоб звичайні рядки випадково не були перетворені на BigInt.
Map та SetMap і Set не мають стандартного JSON-представлення. Якщо передати їх без додаткової обробки, зазвичай буде отримано порожній об’єкт.
const data = {
tags: new Set(["js", "json"]),
scores: new Map([["olena", 95]])
};
console.log(JSON.stringify(data));
// {"tags":{},"scores":{}}Їх потрібно перетворити на масиви самостійно:
const data = {
tags: new Set(["js", "json"]),
scores: new Map([["olena", 95]])
};
const json = JSON.stringify(data, (key, value) => {
if (value instanceof Set) {
return [...value];
}
if (value instanceof Map) {
return [...value.entries()];
}
return value;
});
console.log(json);
// {"tags":["js","json"],"scores":[["olena",95]]}Після парсингу це будуть масиви. Щоб отримати Set або Map, їх потрібно створити вручну або за допомогою reviver.
Поширений спосіб створити просту копію даних:
const original = {
name: "Олена",
preferences: {
theme: "dark"
}
};
const copy = JSON.parse(JSON.stringify(original));
copy.preferences.theme = "light";
console.log(original.preferences.theme);
// darkЦей підхід створює незалежну копію для даних, які повністю сумісні з JSON. Однак він має суттєві обмеження:
губляться функції;
Date перетворюється на рядок;
undefined зникає;
Map, Set та інші спеціальні об’єкти не відновлюються автоматично;
NaN та нескінченності перетворюються на null;
циклічні посилання спричиняють помилку;
BigInt спричиняє помилку.
Тому такий спосіб підходить лише для простих JSON-сумісних структур.
У сучасному JavaScript для глибокого копіювання різних типів також можна використовувати structuredClone(), якщо середовище виконання його підтримує. Це інший механізм, не заснований на JSON, і його не слід плутати із серіалізацією для мережі або збереження.
Об’єкт має циклічне посилання, якщо прямо або опосередковано посилається сам на себе.
const user = {
name: "Олена"
};
user.account = user;
JSON.stringify(user);
// TypeError: Converting circular structure to JSONJSON не може безпосередньо описати таку структуру. Перед серіалізацією потрібно:
видалити циклічні посилання;
перетворити їх на ідентифікатори;
реалізувати спеціальний формат серіалізації.
Наприклад, можна виключити властивість із циклом:
const user = {
id: 1,
name: "Олена"
};
user.self = user;
const json = JSON.stringify(user, (key, value) => {
if (key === "self") {
return undefined;
}
return value;
});
console.log(json);
// {"id":1,"name":"Олена"}toJSON()Якщо об’єкт має метод toJSON(), JSON.stringify() використає результат цього методу замість самого об’єкта.
const session = {
userId: 42,
secretToken: "private-token",
toJSON() {
return {
userId: this.userId
};
}
};
console.log(JSON.stringify(session));
// {"userId":42}Це зручно, коли потрібно визначити публічне JSON-представлення об’єкта. Водночас треба пам’ятати, що toJSON() впливає на всі виклики JSON.stringify() для цього об’єкта.
JSON-об’єкт має кілька відмінностей від JavaScript-об’єкта:
ключі JSON-об’єкта завжди є рядками;
рядки в JSON записуються в подвійних лапках;
у JSON не можна використовувати коментарі;
у JSON не можна використовувати undefined;
у JSON немає функцій, символів, Date, Map, Set та BigInt як окремих стандартних типів;
JSON не підтримує циклічні посилання.
Наприклад, у JavaScript числовий ключ автоматично стає рядком під час серіалізації:
const object = {
10: "десять"
};
const json = JSON.stringify(object);
console.log(json);
// {"10":"десять"}JSON сам по собі не виконує JavaScript-код. Проте небезпечно використовувати eval() для читання JSON:
// Не робіть так:
const data = eval(`(${json})`);Для цього призначений JSON.parse():
const data = JSON.parse(json);Також після парсингу зовнішніх даних потрібно перевіряти їхню структуру. Коректний JSON не гарантує, що він містить очікувані властивості або правильні типи.
const json = '{"age":"тридцять"}';
const data = JSON.parse(json);
if (typeof data.age !== "number") {
throw new TypeError("Властивість age повинна бути числом");
}const order = {
id: 1001,
customer: "Олена",
items: [
{ name: "Клавіатура", quantity: 1, price: 2500 },
{ name: "Миша", quantity: 2, price: 800 }
],
createdAt: new Date("2026-07-25T10:30:00.000Z"),
internalNote: "Не показувати клієнту"
};
// Вибираємо дані для публічного представлення замовлення
const json = JSON.stringify(order, (key, value) => {
if (key === "internalNote") {
return undefined;
}
return value;
}, 2);
console.log("Серіалізовані дані:");
console.log(json);
// Відновлюємо дату після читання JSON
const restoredOrder = JSON.parse(json, (key, value) => {
if (key === "createdAt") {
return new Date(value);
}
return value;
});
console.log("Клієнт:", restoredOrder.customer);
console.log("Кількість позицій:", restoredOrder.items.length);
console.log("Дата є об’єктом Date:", restoredOrder.createdAt instanceof Date);JSON.parse() для JavaScript-об’єктаJSON.parse() очікує рядок:
const user = {
name: "Олена"
};
// Це помилка:
JSON.parse(user);Спочатку об’єкт потрібно серіалізувати або використовувати його без парсингу:
const json = JSON.stringify(user);
const restoredUser = JSON.parse(json);JSON — це рядок, тому в нього немає властивостей об’єкта:
const json = '{"name":"Олена"}';
console.log(json.name);
// undefinedСпочатку потрібно виконати парсинг:
const user = JSON.parse(json);
console.log(user.name);
// Оленаconst json = '{"createdAt":"2026-07-25T10:30:00.000Z"}';
const data = JSON.parse(json);
console.log(data.createdAt instanceof Date);
// falseПотрібно явно створити Date через reviver або після парсингу.
Метод JSON.parse(JSON.stringify(value)) не є універсальним клонуванням. Він підходить лише для даних, які не містять спеціальних типів і циклічних посилань.
Необроблений JSON.parse() може зупинити виконання програми. Для ненадійних джерел даних використовуйте try...catch.
Паролі, токени й приватні ключі не повинні потрапляти до логів, localStorage або публічних відповідей API. Перед серіалізацією перевіряйте склад об’єкта та видаляйте конфіденційні властивості.
JSON.stringify() перетворює JavaScript-значення на JSON-рядок.
JSON.parse() перетворює JSON-рядок назад на JavaScript-значення.
replacer дозволяє вибирати або змінювати дані під час серіалізації.
reviver дозволяє змінювати або відновлювати дані під час парсингу.
Date серіалізується як рядок і не відновлюється в Date автоматично.
undefined, функції та символи в об’єктах пропускаються, а в масивах замінюються на null.
NaN, Infinity та -Infinity перетворюються на null.
BigInt і циклічні посилання спричиняють помилку без спеціальної обробки.
JSON не підходить для збереження всіх JavaScript-типів без втрат.
Дані після JSON.parse() потрібно перевіряти, особливо якщо вони надійшли із зовнішнього джерела.