JSON
Об'єкт – простір імен JSON
містить статичні методи для розбору значень та перетворення значень в запис об'єктів JavaScript (JSON).
Опис
На відміну від більшості глобальних об'єктів, JSON
не є конструктором. Його не можна використовувати з оператором new
або викликати об'єкт JSON
як функцію. Всі властивості та методи JSON
є статичними (так само в об'єкта Math
).
Різниця між JavaScript і JSON
JSON – це синтаксис для серіалізації об'єктів, масивів, чисел, рядків, булевих значень і null
. Він заснований на синтаксисі JavaScript, але відрізняється від нього: більшість JavaScript не є JSON. Наприклад:
- Об'єкти та масиви
Назви властивостей повинні бути рядками в подвійних лапках; коми в кінці заборонені.
- Числа
Нулі на початку – заборонені. Після десяткової крапки має бути принаймні одна цифра.
NaN
таInfinity
– не підтримуються.
Будь-який текст JSON є дійсним виразом JavaScript, але лише після перегляду надмножини JSON. До цього перегляду символи U+2028 LINE SEPARATOR та U+2029 PARAGRAPH SEPARATOR були дозволені в літералах рядків та ключах властивостей в JSON, але таке ж використання в літералах рядків JavaScript викликало SyntaxError
.
Серед інших відмінностей – дозволені лише рядки в подвійних лапках, немає підтримки undefined
і коментарів. Для тих, хто хоче використовувати більш дружній для людини формат налаштувань на основі JSON, є JSON5, який використовує компілятор Babel, а також більш поширений формат YAML.
Той самий текст може представляти різні значення в об'єктному літералі JavaScript і JSON. Більше про це – в розділі Запис літералів об'єктів і JSON.
Повна граматика JSON
Дійсний синтаксис JSON формально описаний наступною граматикою, вираженою в форматі Доповненої нотації Бекуса – Наура, і скопійований зі стандарту IETF JSON (RFC):
JSON-text = object / array
begin-array = ws %x5B ws ; [ left square bracket
begin-object = ws %x7B ws ; { left curly bracket
end-array = ws %x5D ws ; ] right square bracket
end-object = ws %x7D ws ; } right curly bracket
name-separator = ws %x3A ws ; : colon
value-separator = ws %x2C ws ; , comma
ws = *(
%x20 / ; Space
%x09 / ; Horizontal tab
%x0A / ; Line feed or New line
%x0D ; Carriage return
)
value = false / null / true / object / array / number / string
false = %x66.61.6c.73.65 ; false
null = %x6e.75.6c.6c ; null
true = %x74.72.75.65 ; true
object = begin-object [ member *( value-separator member ) ]
end-object
member = string name-separator value
array = begin-array [ value *( value-separator value ) ] end-array
number = [ minus ] int [ frac ] [ exp ]
decimal-point = %x2E ; .
digit1-9 = %x31-39 ; 1-9
e = %x65 / %x45 ; e E
exp = e [ minus / plus ] 1*DIGIT
frac = decimal-point 1*DIGIT
int = zero / ( digit1-9 *DIGIT )
minus = %x2D ; -
plus = %x2B ; +
zero = %x30 ; 0
string = quotation-mark *char quotation-mark
char = unescaped /
escape (
%x22 / ; " quotation mark U+0022
%x5C / ; \ reverse solidus U+005C
%x2F / ; / solidus U+002F
%x62 / ; b backspace U+0008
%x66 / ; f form feed U+000C
%x6E / ; n line feed U+000A
%x72 / ; r carriage return U+000D
%x74 / ; t tab U+0009
%x75 4HEXDIG ) ; uXXXX U+XXXX
escape = %x5C ; \
quotation-mark = %x22 ; "
unescaped = %x20-21 / %x23-5B / %x5D-10FFFF
HEXDIG = DIGIT / %x41-46 / %x61-66 ; 0-9, A-F, or a-f
; HEXDIG equivalent to HEXDIG rule in [RFC5234]
DIGIT = %x30-39 ; 0-9
; DIGIT equivalent to DIGIT rule in [RFC5234]
Несуттєві пробіли можуть зустрічатися всюди, крім всередині JSONNumber
(числа не вміщають пробілів) і JSONString
(там вони тлумачаться як відповідні символи в рядку, або ж викликають помилку). Символ табуляції (U+0009), повернення каретки (U+000D), переведення рядка (U+000A) і пробіл (U+0020) – єдині допустимі пробільні символи.
Статичні властивості
JSON[Symbol.toStringTag]
Початкове значення властивості
[Symbol.toStringTag]
– рядок"JSON"
. Ця властивість використовується вObject.prototype.toString()
.
Статичні методи
JSON.isRawJSON()
Перевіряє, чи є значення об'єктом, поверненим
JSON.rawJSON()
.JSON.parse()
Розібрати порцію рядкового тексту як JSON, необов'язково перетворюючи отримане значення та його властивості, і повернути значення.
JSON.rawJSON()
Створює об'єкт "необробленого JSON", що вміщає уривок тексту JSON. При серіалізації в JSON об'єкт необробленого JSON тлумачиться як вже готовий уривок JSON. Цей текст повинен бути дійсним JSON.
JSON.stringify()
Повернути рядок JSON, що відповідає переданому значенню, необов'язково включаючи лише певні властивості або замінюючи значення властивостей у спосіб, визначений користувачем.
Приклади
Приклад JSON
{
"browsers": {
"firefox": {
"name": "Firefox",
"pref_url": "about:config",
"releases": {
"1": {
"release_date": "2004-11-09",
"status": "retired",
"engine": "Gecko",
"engine_version": "1.7"
}
}
}
}
}
Метод JSON.parse()
можна використовувати для перетворення рядка JSON вище на об'єкт JavaScript:
const jsonText = `{
"browsers": {
"firefox": {
"name": "Firefox",
"pref_url": "about:config",
"releases": {
"1": {
"release_date": "2004-11-09",
"status": "retired",
"engine": "Gecko",
"engine_version": "1.7"
}
}
}
}
}`;
console.log(JSON.parse(jsonText));
Серіалізація чисел без втрат
JSON може вміщати числові літерали довільної точності. Однак не можна представити з повною точністю в JavaScript усі числа JSON, оскільки JavaScript використовує представлення з рухомою комою, яке має фіксовану точність. Наприклад, у JavaScript 12345678901234567890 === 12345678901234567000
, оскільки ці числа мають одне й те ж представлення з рухомою комою. Це означає, що в JavaScript немає числа, яке точно відповідало б числу JSON 12345678901234567890
.
Припустімо, що є точне представлення деякого числа (чи то через BigInt
, чи через власну бібліотеку):
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
JSON.stringify()
.
// Використання методу 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 без втрати точності, не створюючи спершу числове значення (що призвело б до втрати точності) шляхом використання JSON.rawJSON()
, щоб точно вказати, яким має бути вихідний текст JSON.
// Використання методу 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
JSON.parse()
, щоб самостійно розібрати число заново.
const parsedData = JSON.parse(str, (key, value, context) => {
if (key === "gross_gdp") {
// Або скористатися конструктором власної бібліотеки чисел високої точності
return BigInt(context.source);
}
return value;
});
// { gross_gdp: 12345678901234567890n }
Специфікації
Сумісність із браузерами
desktop | mobile | server | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
JSON
|
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 |
JavaScript is a superset of JSON | Chrome Full support 66 | Edge Full support 79 | Firefox Full support 62 | Internet Explorer No support Ні | Opera Full support 53 | Safari Full support 12 | WebView Android Full support 66 | Chrome Android Full support 66 | Firefox for Android Full support 62 | Opera Android Full support 47 | Safari on iOS Full support 12 | Samsung Internet Full support 9.0 | Deno Full support 1.0 | Node.js Full support 10.0.0 |
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 |
stringify
|
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 |
Strings are escaped to well-formed UTF-8
|
Chrome Full support 72 | Edge Full support 79 | Firefox Full support 64 | Internet Explorer No support Ні | Opera Full support 60 | Safari Full support 12.1 | WebView Android Full support 72 | Chrome Android Full support 72 | Firefox for Android Full support 64 | Opera Android Full support 50 | Safari on iOS Full support 12.2 | Samsung Internet Full support 11.0 | Deno Full support 1.0 | Node.js Full support 12.0.0 |