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
Chrome Edge Firefox Internet Explorer Opera Safari WebView Android Chrome Android Firefox for Android Opera Android Safari on iOS Samsung Internet Deno Node.js
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

Дивіться також