Skip to content

Commit

Permalink
update(JS): web/javascript/reference/global_objects/json
Browse files Browse the repository at this point in the history
  • Loading branch information
undead404 committed Mar 21, 2024
1 parent a401013 commit eb9692a
Showing 1 changed file with 85 additions and 0 deletions.
85 changes: 85 additions & 0 deletions files/uk/web/javascript/reference/global_objects/json/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,12 @@ DIGIT = %x30-39 ; 0-9

## Статичні методи

- {{jsxref("JSON.isRawJSON()")}}
- : Перевіряє, чи є значення об'єктом, поверненим {{jsxref("JSON.rawJSON()")}}.
- {{jsxref("JSON.parse()")}}
- : Розібрати порцію рядкового тексту як JSON, необов'язково перетворюючи отримане значення та його властивості, і повернути значення.
- {{jsxref("JSON.rawJSON()")}}
- : Створює об'єкт "необробленого JSON", що вміщає уривок тексту JSON. При серіалізації в JSON об'єкт необробленого JSON тлумачиться як вже готовий уривок JSON. Цей текст повинен бути дійсним JSON.
- {{jsxref("JSON.stringify()")}}
- : Повернути рядок JSON, що відповідає переданому значенню, необов'язково включаючи лише певні властивості або замінюючи значення властивостей у спосіб, визначений користувачем.

Expand Down Expand Up @@ -145,6 +149,87 @@ const jsonText = `{
console.log(JSON.parse(jsonText));
```

### Серіалізація чисел без втрат

JSON може вміщати числові літерали довільної точності. Однак не можна представити з повною точністю в JavaScript усі числа JSON, оскільки JavaScript використовує представлення з рухомою комою, яке має фіксовану точність. Наприклад, у JavaScript `12345678901234567890 === 12345678901234567000`, оскільки ці числа мають одне й те ж представлення з рухомою комою. Це означає, що в JavaScript немає числа, яке точно відповідало б числу JSON `12345678901234567890`.

Припустімо, що є точне представлення деякого числа (чи то через {{jsxref("BigInt")}}, чи через власну бібліотеку):

```js
const data = {
// Тут для зберігання точного значення використовується BigInt,
// але можна використовувати також власну бібліотеку чисел високої точності,
// якщо число може бути не цілим.
gross_gdp: 12345678901234567890n,
};
```

Є потреба його серіалізувати, а потім розібрати на точно те саме число. Є кілька складнощів:

- З боку серіалізації, щоб отримати число в JSON, необхідно передати в `JSON.stringify` саме число, або через функцію `replacer`, або через метод `toJSON`. Але в обох випадках точність втрачається вже під час перетворення цього числа. Якщо передати рядок в `JSON.stringify`, він буде серіалізований як рядок, а не як число.
- З боку розбору, не всі числа можна представити точним чином. Наприклад, `JSON.parse("12345678901234567890")` повертає `12345678901234568000`, оскільки число округлюється до найближчого числа, яке можна представити. Навіть якщо скористатися функцією `reviver`, число вже буде округлене до відповідного числа до того, як функція `reviver` буде викликана.

Загалом, є два способи забезпечити перетворення чисел у JSON і розбір назад без втрат: один з них залучає число JSON, а інший – рядок JSON. JSON – це _комунікаційний формат_, тож якщо використовується JSON, то, ймовірно, відбувається комунікація з іншою системою (запит HTTP, збереження у базі даних тощо). Найкраще рішення в конкретній ситуації залежить від системи-одержувача.

#### Використання рядків JSON

Якщо система-одержувач не має таких же можливостей обробки JSON, як JavaScript, і не підтримує числа високої точності, можна серіалізувати число як рядок, а потім обробити його як рядок на стороні одержувача. Також цей варіант є єдиним можливим у старих версіях JavaScript.

Щоб задати те, як власні типи даних (в тому числі `BigInt`) повинні бути серіалізовані в JSON, потрібно або додати до власного типу даних метод `toJSON`, або скористатися функцією `replacer` {{jsxref("JSON.stringify()")}}.

```js
// Використання методу toJSON()
BigInt.prototype.toJSON = function () {
return this.toString();
};
const str1 = JSON.stringify(data);

// Використання JSON.stringify() з замінювачем
const str2 = JSON.stringify(data, (key, value) => {
if (key === "gross_gdp") {
return value.toString();
}
return value;
});
```

В обох випадках текст JSON матиме вигляд `{"gross_gdp":"12345678901234567890"}`, де значення є рядком, а не числом. Потім на стороні одержувача можна розібрати JSON і обробити рядок.

#### Використання чисел JSON

Якщо одержувач повідомлення нативно підтримує числа високої точності (наприклад, цілі числа Python), передача чисел у вигляді чисел JSON, очевидно, є кращим підходом, адже тоді одержувач може безпосередньо розібрати їх у тип високої точності, а не розбирати рядок з JSON, а потім розбирати число з рядка. У JavaScript можна серіалізувати довільні типи даних у числа JSON без втрати точності, не створюючи спершу числове значення (що призвело б до втрати точності) шляхом використання {{jsxref("JSON.rawJSON()")}}, щоб точно вказати, яким має бути вихідний текст JSON.

```js
// Використання методу toJSON()
BigInt.prototype.toJSON = function () {
return JSON.rawJSON(this.toString());
};
const str1 = JSON.stringify(data);

// Використання JSON.stringify() з замінювачем
const str2 = JSON.stringify(data, (key, value) => {
if (key === "gross_gdp") {
return JSON.rawJSON(value.toString());
}
return value;
});
```

Текст, переданий до `JSON.rawJSON`, тлумачиться як вже готовий уривок JSON, тому він не буде серіалізований як рядок. Таким чином, текст JSON буде мати вигляд `{"gross_gdp":12345678901234567890}`, де значення є числом. Цей JSON одержувач може розібрати без будь-якої додаткової обробки, за умови того, що система-одержувач не має таких же обмежень точності, як JavaScript.

При розбиранні JSON, що містить числа високої точності, в JavaScript, слід дотримуватись особливої обережності, тому що коли `JSON.parse()` закликає функцію `reviver`, то отримане значення вже розібране (і вже втратило точність). Можна скористатися параметром `context.source` функції `reviver` {{jsxref("JSON.parse()")}}, щоб самостійно розібрати число заново.

```js
const parsedData = JSON.parse(str, (key, value, context) => {
if (key === "gross_gdp") {
// Або скористатися конструктором власної бібліотеки чисел високої точності
return BigInt(context.source);
}
return value;
});
// { gross_gdp: 12345678901234567890n }
```

## Специфікації

{{Specifications}}
Expand Down

0 comments on commit eb9692a

Please sign in to comment.