JSON — JavaScript Object Notation, «нотация JavaScript-объектов». Это текстовый формат для представления структурированных данных. Если вы видели объект в JavaScript, вы уже почти знаете JSON.

Как выглядит JSON

{
  "id": 42,
  "title": "Заметка",
  "isPublished": true,
  "views": null,
  "tags": ["css", "html"],
  "author": {
    "name": "Anna",
    "age": 28
  }
}

В JSON есть всего шесть типов значений:

  • строка — всегда в двойных кавычках: "hello";
  • число — целое или дробное, без кавычек: 423.14;
  • булево — true или false;
  • null — null, «значения нет»;
  • массив — список значений в квадратных скобках: [1, 2, 3];
  • объект — пары «ключ: значение» в фигурных скобках, как пример выше.

Вложенность любой глубины — объект внутри массива внутри объекта внутри массива. Сервер вернёт — разберёте.

Где JSON отличается от JavaScript-объектов

Многие новички путают JSON с JS-объектами и пишут в JSON-файле такое, что валидатор сразу ругается. Запомните три отличия:

  • Ключи всегда в двойных кавычках. Не одинарных, не без кавычек. В JavaScript {name: "Anna"} валидно, в JSON {"name": "Anna"} обязательно.
  • Никаких комментариев. Ни //, ни /* */. JSON — чисто формат данных, не код.
  • Никаких висящих запятых после последнего элемента. В JavaScript [1, 2, 3,] допустимо, в JSON — синтаксическая ошибка.

Ещё в JSON нет undefinedfunctionDateMapSet. Только шесть типов, перечисленных выше. Если в JS-объекте было поле undefined, после JSON.stringify оно просто исчезнет. Date превратится в строку. Функции выкинутся.

Откуда взялся JSON и почему он победил

До JSON в вебе для обмена данными между клиентом и сервером использовался XML. Тот же пост выглядел бы примерно так:

<post>
  <id>42</id>
  <title>Заметка</title>
  <tags>
    <tag>css</tag>
    <tag>html</tag>
  </tags>
</post>

Если посчитать символы, JSON получается короче. На клиенте парсинг XML требует библиотек или громоздкого DOMParser. JSON в JavaScript парсится одной встроенной функцией: JSON.parse. Плюс структура JSON один в один ложится на JS-объекты — никаких атрибутов отдельно от вложенных тегов, никаких пространств имён.

За пятнадцать лет JSON практически вытеснил XML из веб-API. XML ещё встречается в корпоративных системах, банковских интеграциях, SOAP-сервисах — но новые REST API почти всегда говорят на JSON.

JSON.parse и JSON.stringify

В браузере и в Node.js есть две встроенные функции для работы с JSON.

JSON.parse(текст) — превращает JSON-строку в JS-значение:

const text = '{"id": 42, "title": "Заметка"}';
const post = JSON.parse(text);

post.id;     // 42
post.title;  // "Заметка"

JSON.stringify(значение) — превращает JS-значение в JSON-строку:

const data = { id: 42, title: "Заметка", tags: ["css"] };
const text = JSON.stringify(data);
// '{"id":42,"title":"Заметка","tags":["css"]}'

В контексте fetch мы будем встречать обе:

  • response.json() — читает тело ответа и сразу зовёт JSON.parse под капотом. Возвращает уже готовый JS-объект.
  • body: JSON.stringify(...) — превращает JS-объект в JSON-строку и кладёт её в тело запроса.

Это два самых частых вызова в коде, который работает с REST API. Привыкайте.

Типичные ошибки JSON

На что наступают новички:

  • «Unexpected token» при парсинге. Скорее всего, в ответе пришёл не JSON. Часто это HTML-страница с ошибкой сервера. Проверьте статус и Content-Type ответа перед response.json().
  • «Unexpected end of JSON input». Тело пустое (статус 204) или оборвано. Не зовите .json() на ответах, в которых тела нет.
  • Объект превратился в "[object Object]". Где-то забыли JSON.stringify и отправили объект сразу в bodyfetch привёл его к строке через toString(), получился мусор.
  • Поле undefined исчезло после JSON.stringify. Это не баг, это спецификация. Если поле важное, явно ставьте null.

На этом теоретическая база готова. В следующем модуле разберём набор HTTP-методов и статус-кодов — после чего можно будет наконец писать код.