Оригінальна публікація: JSON Stringify Example – How to Parse a JSON Object with JS

JSON, або JavaScript Object Notation (укр. запис об’єктів JavaScript), оточує нас всюди. Якщо ви коли-небудь користувалися вебзастосунком, є велика ймовірність, що він використовував JSON для структурування, зберігання та передачі даних між своїми серверами та вашим пристроєм.

У цій публікації ми коротко розглянемо відмінності між JSON та JavaScript, а потім перейдемо до різних способів парсингу JSON за допомогою JavaScript у браузері та проєктах Node.js.

Відмінності між JSON та JavaScript

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

Розглянемо об’єкти JSON і масиви JSON та порівняємо їх з відповідниками JavaScript.

Об’єкти JSON vs літерали об’єктів JavaScript

По-перше, ось об’єкт JSON:

{
  "name": "Jane Doe",
  "favorite-game": "Stardew Valley",
  "subscriber": false
}
jane-profile.json

Основна відмінність між об’єктом JSON та звичайним об’єктом JavaScript, який також називають літералом об’єкта, полягає у лапках. Всі ключі та значення рядків в об’єкті JSON повинні бути в лапках (").

Літерали об’єктів JavaScript трохи гнучкіші. В об’єктних літералах вам не потрібно брати ключі та рядки в лапки. Замість цього ви можете використати одинарні лапки (') або взагалі не використовувати лапки для ключів.

Наведений вище код міг би виглядати ось так, якби був літералом об’єкта JavaScript:

const profile = {
  name: 'Jane Doe',
  'favorite-game': 'Stardew Valley',
  subscriber: false
}

Зверніть увагу, що ключ 'favorite-game' в одинарних лапках. Для об’єктних літералів потрібно брати в лапки ключі, де слова розділені тире (-).

Якщо ви хочете уникнути лапок, ви можете переписати ключ, використавши верблюдячий регістр (favoriteGame) або розділивши слова підкресленням (favorite_game).

Масиви JSON vs масиви JavaScript

Масиви JSON працюють майже так само, як масиви в JavaScript, і можуть містити рядки, булеві значення, числа та інші об’єкти JSON. Наприклад:

[
  {
    "name": "Jane Doe",
    "favorite-game": "Stardew Valley",
    "subscriber": false
  },
  {
    "name": "John Doe",
    "favorite-game": "Dragon Quest XI",
    "subscriber": true
  }
]
profiles.json

У звичайному JavaScript це може виглядати так:

const profiles = [
  {
    name: 'Jane Doe',
    'favorite-game': 'Stardew Valley',
    subscriber: false
  },
  {
    name: 'John Doe',
    'favorite-game': 'Dragon Quest XI',
    subscriber: true
  }
];

JSON як рядок

Може виникнути запитання: якщо існують об’єкти й масиви JSON, чи не можна було б використовувати їх у своїй програмі як звичайний літерал об’єкта чи масив JavaScript?

Так не можна зробити, оскільки насправді JSON — це простий рядок.

Наприклад, коли ви пишете JSON в окремому файлі, як-от згадані вище jane-profile.json чи profiles.json, цей файл насправді містить текст у формі об’єкта JSON або масиву, який виглядає як JavaScript.

І якщо виконати запит до API, повернеться щось схоже:

{"name":"Jane Doe","favorite-game":"Stardew Valley","subscriber":false}

Так само як і з текстовими файлами, якщо ви хочете використати JSON у своєму проєкті, вам потрібно буде проаналізувати або змінити його на щось, зрозуміле вашій мові програмування. Наприклад, парсинг об’єкта JSON в Python створить словник.

З цими знаннями розглянемо різні способи парсингу JSON в JavaScript.

Як парсити JSON у браузері

Якщо ви працюєте з JSON у браузері, ймовірно, ви отримуєте або надсилаєте дані через API.

Розглянемо декілька прикладів.

Як парсити JSON за допомогою fetch

Найпростіший спосіб отримати дані з API — використати fetch, який містить метод .json() для автомагічного парсингу відповідей JSON у придатний для використання літерал об’єкта або масив JavaScript.

Ось деякий код, який використовує fetch, щоб виконати запит GET для жарту з безоплатного Chuck Norris Jokes API:

fetch('https://api.chucknorris.io/jokes/random?category=dev')
  .then(res => res.json()) // метод .json() парсить відповідь JSON у літерал об’єкта JS
  .then(data => console.log(data));

Якщо ви запустите цей код у браузері, то побачите щось схоже на консолі:

{
    "categories": ["dev"],
    "created_at": "2020-01-05 13:42:19.324003",
    "icon_url": "https://assets.chucknorris.host/img/avatar/chuck-norris.png",
    "id": "elgv2wkvt8ioag6xywykbq",
    "updated_at": "2020-01-05 13:42:19.324003",
    "url": "https://api.chucknorris.io/jokes/elgv2wkvt8ioag6xywykbq",
    "value": "Chuck Norris's keyboard doesn't have a Ctrl key because nothing controls Chuck Norris."
}

Хоча це виглядає як об’єкт JSON, насправді це літерал об’єкта JavaScript, і ви можете вільно використовувати його у своїй програмі.

Як перетворити JSON у рядок за допомогою JSON.stringify()

А якщо ви хочете надсилати дані до API?

Скажімо, ви хочете надіслати жарт про Чака Норріса до Chuck Norris Jokes API, щоб інші люди могли прочитати його пізніше.

Спочатку ви повинні написати свій жарт як літерал об’єкта JS:

const newJoke = {
  categories: ['dev'],
  value: "Chuck Norris's keyboard is made up entirely of Cmd keys because Chuck Norris is always in command."
};

Тоді, оскільки ви надсилаєте дані до API, вам потрібно буде перетворити літерал об’єкта newJoke у рядок JSON.

На щастя, JavaScript містить надзвичайно корисний метод для цього — JSON.stringify():

const newJoke = {
  categories: ['dev'],
  value: "Chuck Norris's keyboard is made up entirely of Cmd keys because Chuck Norris is always in command."
};

console.log(JSON.stringify(newJoke)); // {"categories":["dev"],"value":"Chuck Norris's keyboard is made up entirely of Cmd keys because Chuck Norris is always in command."}

console.log(typeof JSON.stringify(newJoke)); // string

Хоча в цьому прикладі ми перетворюємо літерал об’єкта у рядок JSON, JSON.stringify() також працює з масивами.

Вкінці вам просто потрібно буде надіслати свій жарт-рядок JSON назад до API із запитом POST.

Зауважте, що Chuck Norris Jokes API насправді не має цієї функції. Але якби ця функція була, код був би схожим:

const newJoke = {
  categories: ['dev'],
  value: "Chuck Norris's keyboard is made up entirely of Cmd keys because Chuck Norris is always in command."
};

fetch('https://api.chucknorris.io/jokes/submit', { // fake API endpoint
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify(newJoke), // turn the JS object literal into a JSON string
})
  .then(res => res.json())
  .then(data => console.log(data))
  .catch(err => {
    console.error(err);
  });

Таким чином ви проаналізували вхідний JSON із fetch та використали JSON.stringify() для перетворення літералу об’єкта JS у рядок JSON.

Як працювати з локальними файлами JSON у браузері

На жаль, неможливо (або небажано) завантажити локальний файл JSON у браузер.

fetch викине помилку, якщо ви спробуєте завантажити локальний файл. Скажімо, у вас є файл JSON з парою жартів:

[
  {
    "categories": ["dev"],
    "created_at": "2020-01-05 13:42:19.324003",
    "icon_url": "https://assets.chucknorris.host/img/avatar/chuck-norris.png",
    "id": "elgv2wkvt8ioag6xywykbq",
    "updated_at": "2020-01-05 13:42:19.324003",
    "url": "https://api.chucknorris.io/jokes/elgv2wkvt8ioag6xywykbq",
    "value": "Chuck Norris's keyboard doesn't have a Ctrl key because nothing controls Chuck Norris."
  },
  {
    "categories": ["dev"],
    "created_at": "2020-01-05 13:42:19.324003",
    "icon_url": "https://assets.chucknorris.host/img/avatar/chuck-norris.png",
    "id": "ae-78cogr-cb6x9hluwqtw",
    "updated_at": "2020-01-05 13:42:19.324003",
    "url": "https://api.chucknorris.io/jokes/ae-78cogr-cb6x9hluwqtw",
    "value": "There is no Esc key on Chuck Norris' keyboard, because no one escapes Chuck Norris."
  }
]
jokes.json

Ви хочете проаналізувати його та створити список жартів на звичайній сторінці HTML.

Якщо ви створите сторінку з наступним кодом і відкриєте її у своєму браузері:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
    <meta name="viewport" content="width=device-width" />
    <title>Fetch Local JSON</title>
  </head>
  <script>
    fetch("./jokes.json", { mode: "no-cors" }) // disable CORS because path does not contain http(s)
      .then((res) => res.json())
      .then((data) => console.log(data));
  </script>
</html>
index.html

На консолі ви побачите:

Fetch API cannot load file://<path>/jokes.json. URL scheme "file" is not supported

Браузери не дозволяють доступ до локальних файлів за замовчуванням з міркувань безпеки. Це добре, і вам не варто намагатися обійти цю поведінку.

Натомість можна перетворити локальний файл JSON у JavaScript. На щастя, це досить легко, оскільки синтаксис JSON дуже схожий на JavaScript.

Все, що вам потрібно зробити, це створити новий файл і оголосити свій JSON як змінну:

const jokes = [
  {
    "categories": ["dev"],
    "created_at": "2020-01-05 13:42:19.324003",
    "icon_url": "https://assets.chucknorris.host/img/avatar/chuck-norris.png",
    "id": "elgv2wkvt8ioag6xywykbq",
    "updated_at": "2020-01-05 13:42:19.324003",
    "url": "https://api.chucknorris.io/jokes/elgv2wkvt8ioag6xywykbq",
    "value": "Chuck Norris's keyboard doesn't have a Ctrl key because nothing controls Chuck Norris."
  },
  {
    "categories": ["dev"],
    "created_at": "2020-01-05 13:42:19.324003",
    "icon_url": "https://assets.chucknorris.host/img/avatar/chuck-norris.png",
    "id": "ae-78cogr-cb6x9hluwqtw",
    "updated_at": "2020-01-05 13:42:19.324003",
    "url": "https://api.chucknorris.io/jokes/ae-78cogr-cb6x9hluwqtw",
    "value": "There is no Esc key on Chuck Norris' keyboard, because no one escapes Chuck Norris."
  }
]
jokes.js

І додати його на свою сторінку як окремий скрипт:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
    <meta name="viewport" content="width=device-width" />
    <title>Fetch Local JSON</title>
  </head>
  <script src="jokes.js"></script>
  <script>
    console.log(jokes);
  </script>
</html>

Ви зможете вільно використовувати масив jokes у своєму коді.

Ви також можете використовувати модулі JavaScript, щоб зробити те саме, але це виходить за межі публікації.

А якщо ви хочете працювати з локальними файлами JSON і маєте встановлений Node.js? Давайте розглянемо, як це зробити.

Як парсити JSON у Node.js

Node.js — це середовище виконання JavaScript, яке дозволяє запустити JavaScript поза браузером. Ви можете прочитати все про Node.js тут.

Незалежно від того, чи ви використовуєте Node.js для запуску коду локально на своєму комп’ютері чи для запуску цілих вебзастосунків на сервері, корисно знати, як працювати з JSON.

Для наступних прикладів ми використовуватимемо той самий файл jokes.json:

[
  {
    "categories": ["dev"],
    "created_at": "2020-01-05 13:42:19.324003",
    "icon_url": "https://assets.chucknorris.host/img/avatar/chuck-norris.png",
    "id": "elgv2wkvt8ioag6xywykbq",
    "updated_at": "2020-01-05 13:42:19.324003",
    "url": "https://api.chucknorris.io/jokes/elgv2wkvt8ioag6xywykbq",
    "value": "Chuck Norris's keyboard doesn't have a Ctrl key because nothing controls Chuck Norris."
  },
  {
    "categories": ["dev"],
    "created_at": "2020-01-05 13:42:19.324003",
    "icon_url": "https://assets.chucknorris.host/img/avatar/chuck-norris.png",
    "id": "ae-78cogr-cb6x9hluwqtw",
    "updated_at": "2020-01-05 13:42:19.324003",
    "url": "https://api.chucknorris.io/jokes/ae-78cogr-cb6x9hluwqtw",
    "value": "There is no Esc key on Chuck Norris' keyboard, because no one escapes Chuck Norris."
  }
]
jokes.json

Як парсити файл JSON за допомогою require()

Почнемо з найпростішого методу.

Якщо у вас є локальний файл JSON, вам потрібно лише використати require(), щоб завантажити його, як і будь-який інший модуль Node.js:

const jokes = require('./jokes.json');

Файл JSON буде автоматично аналізований, і ви зможете використовувати його у своєму проєкті:

const jokes = require('./jokes.json');

console.log(jokes[0].value); // "Chuck Norris's keyboard doesn't have a Ctrl key because nothing controls Chuck Norris."

Зверніть увагу, що це синхронно, тобто ваша програма зупиниться, доки весь файл не буде проаналізований. Дійсно великі файли JSON можуть спричинити сповільнення вашої програми, тому будьте обережні з цим.

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

Як парсити файл JSON за допомогою fs.readFileSync() та JSON.parse()

Традиційніший спосіб (через відсутність кращого терміну) парсингу файлів JSON у проєктах Node.js — прочитати файл за допомогою fs (файлова система), а потім проаналізувати за допомогою JSON.parse().

Подивимося, як це зробити з методом fs.readFileSync(). Спочатку додайте модуль fs до свого проєкту:

const fs = require('fs');

Потім створіть нову змінну для зберігання виводу файлу jokes.json та встановіть для неї значення fs.readFileSync():

const fs = require('fs');
const jokesFile = fs.readFileSync();

fs.readFileSync() приймає декілька аргументів. Першим є шлях до файлу, який ви хочете прочитати:

const fs = require('fs');
const jokesFile = fs.readFileSync('./jokes.json');

Але якщо зараз ви введете jokesFile на консолі, то побачите щось подібне:

<Buffer 5b 0a 20 20 7b 0a 20 20 20 20 22 63 61 74 65 67 6f 72 69 65 73 22 3a 20 5b 22 64 65 76 22 5d 2c 0a 20 20 20 20 22 63 72 65 61 74 65 64 5f 61 74 22 3a ... 788 more bytes>

Це означає, що модуль fs читає файл, але він не знає кодування чи формат файлу. fs можна використовувати для завантаження майже будь-якого файлу, а не лише текстових, як-от JSON, тому нам потрібно сказати, як файл закодовано.

Для текстових файлів зазвичай використовують utf8:

const fs = require('fs');
const jokesFile = fs.readFileSync('./jokes.json', 'utf8');

Якщо зараз ввести jokesFile на консолі, ви побачите вміст файлу.

Але поки що ми просто читаємо файл, і він досі є рядком. Нам знадобиться використати інший метод, щоб розібрати jokesFile у придатний для використання об’єкт або масив JavaScript.

Для цього ми використаємо JSON.parse():

const fs = require('fs');
const jokesFile = fs.readFileSync('./jokes.json', 'utf8');
const jokes = JSON.parse(jokesFile);

console.log(jokes[0].value); // "Chuck Norris's keyboard doesn't have a Ctrl key because nothing controls Chuck Norris."

Як випливає з назви, JSON.parse() бере рядок JSON та аналізує його в літерал об’єкта або масив JavaScript.

Подібно до наведеного вище методу require, fs.readFileSync() також синхронний метод, а це означає що він може призвести до сповільнення вашої програми, якщо вона читає великий файл (JSON чи інший).

Крім того, він читає файл лише один раз та завантажує його в пам’ять. Якщо файл змінюється, вам потрібно буде прочитати файл знову. Для полегшення ви можете створити функцію для читання файлів.

Ось як це може виглядати:

const fs = require('fs');
const readFile = path => fs.readFileSync(path, 'utf8');

const jokesFile1 = readFile('./jokes.json');
const jokes1 = JSON.parse(jokesFile1);

console.log(jokes1[0].value); // "Chuck Norris's keyboard doesn't have a Ctrl key because nothing controls Chuck Norris."

// the jokes.json file changes at some point

const jokesFile2 = readFile('./jokes.json');
const jokes2 = JSON.parse(jokesFile2);

console.log(jokes2[0].value); // "Chuck Norris's keyboard is made up entirely of Cmd keys because Chuck Norris is always in command."

Як парсити JSON за допомогою fs.readFile() та JSON.parse()

Метод fs.readFile() дуже схожий на fs.readFileSync(), але він працює асинхронно. Це чудово, якщо у вас є великий файл для читання, і ви не хочете, щоб він затримував решту вашого коду.

Ось базовий приклад:

const fs = require('fs');

fs.readFile('./jokes.json', 'utf8');

Поки що це виглядає подібно до того, що ми робили з fs.readFileSync(), за винятком того, що ми не призначаємо його змінній, як-от jokesFile. Оскільки цей метод асинхронний, будь-який код після fs.readFile() буде виконано до завершення читання файлу.

Замість цього ми використаємо функцію зворотного виклику та проаналізуємо JSON у ній:

const fs = require('fs');

fs.readFile('./jokes.json', 'utf8', (err, data) => {
  if (err) console.error(err);
  const jokes = JSON.parse(data);

  console.log(jokes[0].value);
});

console.log("This will run first!");

На консолі буде виведено наступне:

This will run first!
Chuck Norris's keyboard doesn't have a Ctrl key because nothing controls Chuck Norris.

Подібно до fs.readFileSync(), fs.readFile() завантажує файл у пам’ять, а це означає, що вам доведеться прочитати файл знову, якщо він зміниться.

Крім того, всупереч тому, що fs.readFile() є асинхронним, він завантажує весь файл, який читає, у пам’ять. Якщо у вас масивний файл, можливо, краще переглянути потоки Node.js.

Як перетворити JSON у рядок за допомогою JSON.stringify() у Node.js

Зрештою, якщо ви аналізуєте JSON за допомогою Node.js, є великі шанси, що колись вам доведеться повернути JSON як відповідь API.

На щастя, це працює так само, як і в браузері: просто використайте JSON.stringify(), щоб перетворити літерали об’єктів або масивів JavaScript  у рядок JSON:

const newJoke = {
  categories: ['dev'],
  value: "Chuck Norris's keyboard is made up entirely of Cmd keys because Chuck Norris is always in command."
};

console.log(JSON.stringify(newJoke)); // {"categories":["dev"],"value":"Chuck Norris's keyboard is made up entirely of Cmd keys because Chuck Norris is always in command."}

Це все! Ми розглянули практично все, що вам потрібно знати про роботу з JSON у браузері та проєктах Node.js.

А тепер вирушайте та перетворюйте JSON в рядки чи аналізуйте їх скільки душі завгодно.

Я щось пропустив? Як ви аналізуєте JSON у своїх проєктах? Дайте мені знати у твіттері.