JSON.parse()
Статичний метод JSON.parse()
(розібрати) розбирає рядок у форматі JSON, конструюючи значення чи об'єкт JavaScript, описаний рядком. Можна передати функцію як необов'язковий параметр reviver для виконання трансформацій над результівним об'єктом перед його поверненням.
Спробуйте його в дії
Синтаксис
JSON.parse(text)
JSON.parse(text, reviver)
Параметри
text
(текст)Рядок, який буде розібрано як JSON. Опис синтаксису JSON можна знайти в розділі про об'єкт
JSON
.reviver
(відроджувач) Необов'язковеЯкщо є функцією — цей параметр прописує те, як початкове, сконструйоване під час розбору, значення слід трансформувати перед його поверненням. Невикличні значення ігноруються. Функція викликається з наступними аргументами:
key
Ключ, який відповідає значенню.
value
Значення – результат розбору.
context
Необов'язковеОб'єкт контексту, що зберігає стан, який стосується відродження поточного виразу. Цей об'єкт створюється заново для кожного заклику функції-відроджувача. Він передається лише при відродженні примітивних значень, але не тоді, коли
value
є об'єктом або масивом. У цього об'єкта є наступна властивість:source
Вихідний рядок JSON, що представляє це значення.
Повернене значення
Об'єкт
, Масив
, рядок, число, булеве значення або null
, згідно з переданим в text
рядком JSON.
Винятки
SyntaxError
Викидається, якщо рядок до розбору не є дійсним JSON.
Опис
JSON.parse()
розбирає рядок JSON згідно з граматикою JSON, а потім обчислює рядок так, ніби він був виразом JavaScript. Єдиний випадок, за якого дрібка тексту JSON представляє значення, котре відрізняється від такого самого виразу JavaScript – обробка ключа "__proto__"
: дивіться Синтаксис літерала об'єкта і JSON.
Параметр reviver
Якщо заданий reviver
(відновник), то значення, обчислене при розборі, перетворюється перед поверненням. А саме, обчислене значення і всі його властивості (проходом в глибину), починаючи від властивостей найбільшої вкладеності, й аж до самого кореневого значення – пропускаються крізь reviver
.
Параметр reviver
при виклику отримує об'єкт, котрий містить властивість, що обробляється, як значення this
(якщо не визначити reviver
як стрілкову функцію – тоді окремого зв'язування this
не буде), а також два аргументи: key
та value
, котрі представляють ім'я в рядковому вигляді (навіть коли об'єкт є масивом) і значення властивості. У випадку примітивних значень передається додатковий параметр context
, який містить вихідний текст значення. Якщо функція reviver
повертає undefined
(чи не повертає жодного значення – наприклад, коли виконання звалюється з кінця функції), то властивість видаляється з об'єкта. Інакше – властивість перевизначається з поверненим значенням. Якщо reviver
перетворює лише частину значень, то слід пересвідчитися, що всі неперетворені значення повертаються як було: інакше вони будуть видалені з результівного об'єкта.
Подібно до параметра JSON.stringify()
replacer
, щодо масивів і об'єктів, reviver
в останню чергу викликається на кореневому об'єкті з порожнім рядком як key
й кореневим об'єктом як value
. Для решти дійсних значень JSON reviver
працює схоже та викликається один раз із порожнім рядком за key
та самим значенням за value
.
Якщо повернути з reviver
інше значення, то це нове значення повністю замінить вихідне значення, що розбиралося. Така логіка застосовується навіть до кореневого значення. Наприклад:
const transformedObj1 = JSON.parse('[1,5,{"s":1}]', (key, value) => {
return typeof value === "object" ? undefined : value;
});
console.log(transformedObj1); // undefined
Немає узагальненого способу це обійти. Не можна окремо обробляти випадок, коли key
є порожнім рядком, тому що об'єкти JSON також можуть містити ключі, що є порожніми рядками. При реалізації відновника потрібно дуже точно знати, яке перетворення потрібно для кожного ключа.
Зверніть увагу, що reviver
спрацьовує після розбору значення. Тому, наприклад, числа в тексті JSON вже будуть перетворені на числа JavaScript, в процесі чого можуть втратити частину точності. Одним зі способів передачі великих чисел без втрати точності є серіалізація їх як рядків, а потім відродження до BigInt або іншого відповідного формату довільної точності.
Також можна скористатися властивістю context.source
, щоб звернутися до вихідного тексту JSON, який представляє значення, як показано нижче:
const bigJSON = '{"gross_gdp": 12345678901234567890}';
const bigObj = JSON.parse(bigJSON, (key, value, context) => {
if (key === "gross_gdp") {
// Ігнорувати значення, оскільки воно вже втратило точність
return BigInt(context.source);
}
return value;
});
Приклади
Застосування JSON.parse()
JSON.parse("{}"); // {}
JSON.parse("true"); // true
JSON.parse('"foo"'); // "foo"
JSON.parse('[1, 5, "false"]'); // [1, 5, "false"]
JSON.parse("null"); // null
Застосування параметра reviver
JSON.parse(
'{"p": 5}',
(key, value) =>
typeof value === "number"
? value * 2 // повертає значення * 2 для чисел
: value, // повертає все інше незмінним
);
// { p: 10 }
JSON.parse('{"1": 1, "2": 2, "3": {"4": 4, "5": {"6": 6}}}', (key, value) => {
console.log(key);
return value;
});
// 1
// 2
// 4
// 6
// 5
// 3
// ""
Використання reviver у парі з replacer у JSON.stringify()
Для коректної роботи зі значенням (щоб воно десеріалізувалось до такого самого вихідного об'єкта), процес серіалізації мусить зберігати інформацію про типи. Наприклад, можна застосувати для цього параметр JSON.stringify()
replacer
:
// Map в загальному випадку серіалізуються як об'єкти без властивостей.
// Можна застосувати замінювач, аби вказати, які записи повинні бути серіалізовані.
const map = new Map([
[1, "один"],
[2, "два"],
[3, "три"],
]);
const jsonText = JSON.stringify(map, (key, value) =>
value instanceof Map ? Array.from(value.entries()) : value,
);
console.log(jsonText);
// [[1,"один"],[2,"два"],[3,"три"]]
const map2 = JSON.parse(jsonText, (key, value) =>
Array.isArray(value) ? new Map(value) : value,
);
console.log(map2);
// Map { 1 => "один", 2 => "два", 3 => "три" }
Через те, що JSON не має синтаксичного простору для метаданих з анотаціями типу, для відновлення значень, що не є звичайними об'єктами, слід розглянути наступні варіанти:
- Серіалізувати увесь об'єкт у рядок і додати префікс у вигляді тегу типу.
- "Вгадувати" на основі структури даних (наприклад, масив з масивів двох значень)
- Якщо форма даних – фіксована, то на основі імені властивості (наприклад, всі властивості, що звуться
registry
, містять об'єктиMap
).
Неприйнятний JSON
Коли функція JSON.parse
отримує рядок, що не відповідає граматиці JSON, вона викидає помилку SyntaxError
.
Масиви та об'єкти у JSON не можуть мати висячих ком:
JSON.parse("[1, 2, 3, 4, ]");
// SyntaxError: Unexpected token ] in JSON at position 13
JSON.parse('{"foo": 1, }');
// SyntaxError: Unexpected token } in JSON at position 12
Рядки у JSON повинні бути відмежовані подвійними (але не одинарними) лапками:
JSON.parse("{'foo': 1}");
// SyntaxError: Unexpected token ' in JSON at position 1
JSON.parse("'string'");
// SyntaxError: Unexpected token ' in JSON at position 0
Коли JSON записується всередині рядкового літерала JavaScript, слід або користуватися одинарними лапками для відмежовування всього літерала, або екранувати подвійні лапки, що відмежовують рядки всередині JSON:
JSON.parse('{"foo": 1}'); // OK
JSON.parse("{\"foo\": 1}"); // OK
Специфікації
Сумісність із браузерами
desktop | mobile | server | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
parse
|
Chrome Full support 3 | Edge Full support 12 | Firefox Full support 3.5 | Internet Explorer Full support 8 | Opera Full support 10.5 | Safari Full support 4 | WebView Android Full support 37 | Chrome Android Full support 18 | Firefox for Android Full support 4 | Opera Android Full support 11 | Safari on iOS Full support 4 | Samsung Internet Full support 1.0 | Deno Full support 1.0 | Node.js Full support 0.10.0 |