JavaRush /Курси /Java Server /Усі люблять JSON

Усі люблять JSON

Java Server
Рівень 10 , Лекція 0
Відкрита

1. В HTTP body потрібен формат даних

Якщо дивитися на HTTP тверезо (а інколи це вже досягнення), то body — це просто шматок даних. Не «JSON за замовчуванням», не «рядок», не «об’єкт», а набір байтів. У цей набір можна покласти що завгодно: картинку, HTML-сторінку, PDF, бінарний архів, текст «OK» і навіть поему на 3 000 рядків. Але API — не поетичний гурток. Йому потрібен формат, який однозначно описує структуру даних.

У звичайному бекенд-API ми майже завжди хочемо передавати структуровані дані: «ось книга, у неї є title і author», «ось список елементів, і їх count», «ось помилка, і в неї є errorCode та message». Якщо передавати це як довільний текст, доведеться вручну домовлятися про правила: «перше слово — це title, друге — author, а якщо в назві є пробіл, то все, тримайся». Такі домовленості ламаються від першої ж зміни. Тому індустрія доволі швидко дійшла до ідеї: давайте використовувати формат, де структура даних виражена явно, а не вгадується за позицією слова.

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

Невеликий фрагмент на Java, просто щоб зафіксувати ідею «формат body — частина договору»:

String contentType = "application/json";

// Блок тексту читабельніший для JSON, ніж екранування лапок в одному рядку
String responseBody = """
    { "status": "UP", "appName": "readlater-api" }
    """;

// Content-Type каже: "це JSON, розбирай його за правилами JSON"
System.out.println(contentType);         // application/json
System.out.println(responseBody.strip()); // { "status": "UP", "appName": "readlater-api" }

Зверніть увагу: тут важливі обидва значення. responseBody без Content-Type — це просто рядок. Content-Type без responseBody — це обіцянка, але не результат.

2. JSON: «структура в тексті»

У світі є різні формати даних, але JSON здобув популярність в API з доволі прозаїчної причини: він зручний майже всім учасникам процесу. Клієнту зручно, бо JSON легко розпарсити практично в будь-якій мові. Серверу зручно, бо JSON легко генерувати. Розробнику зручно, бо JSON можна відкрити в Postman, побачити очима й швидко зрозуміти, що взагалі відбувається.

Ключова фішка JSON — він текстовий, але водночас структурований. Це як анкета: з одного боку — просто папірець із текстом, з іншого — у ній є поля, і їхній зміст зрозумілий. Саме ця комбінація «текст + структура» робить JSON чудовим кандидатом для обміну даними через HTTP.

Коли формат даних текстовий, у вас з’являється суперсила дебагу. Будь-яка проблема перетворюється з «щось десь зламалося» на «я бачу конкретний body, бачу конкретні поля й можу порівняти з контрактом». Тому на рівні навчання та перших кроків у бекенд-розробці JSON ідеальний: він допомагає тримати зв’язок між абстракцією «контракт» і реальними байтами в мережі.

Ще одна причина популярності JSON — він добре підходить під типові форми даних бекенд-API. Наприклад, «один об’єкт», «список об’єктів», «помилка з деталями» — усе це природно виражається JSON-структурами. Так, на практиці бувають нюанси, але базовий набір покриває 80 % життя звичайного API.

Щоб відчути різницю між «довільним текстом» і «структурою», порівняйте два рядки. Обидва «читабельні», але лише один має строгий формат:

// Довільний текст: правила формату доводиться додумувати й підтримувати вручну
String plainText = "title=Clean Code; author=Robert C. Martin";

// JSON: структура зафіксована (поля + значення), її можна надійно розбирати
String jsonText = """
    { "title": "Clean Code", "author": "Robert C. Martin" }
    """;

System.out.println(plainText);        // title=Clean Code; author=Robert C. Martin
System.out.println(jsonText.strip()); // { "title": "Clean Code", "author": "Robert C. Martin" }

У першому варіанті ми сподіваємося, що ніхто не вставить ; у назву, і що порядок завжди буде однаковим. У другому варіанті структура фіксується явно: є об’єкт, у нього є поля з іменами, і кожне поле має значення.

3. JSON в HTTP-контракті: заголовки та статус

Спокуса новачка — думати, що «контракт = JSON». Насправді контракт ширший: це і метод, і шлях, і статус-код, і заголовки, і форма body. JSON — просто одна з частин цієї конструкції. І саме тому в API так важливі Content-Type і (з боку клієнта) Accept: вони кажуть, як розуміти тіло повідомлення.

Уявіть, що вам надіслали рядок {"status":"UP"}. Це JSON? Можливо. Це просто текст? Також можливо. Це частина якоїсь іншої структури? Теоретично так. Щоб не гадати, в HTTP є явний маркер формату — заголовок Content-Type. Він робить обмін зрілішим: не «вгадай мелодію за одним байтом», а «візьми оголошений формат».

У житті це виглядає приблизно так: клієнт надсилає запит і каже «я хочу JSON», сервер відповідає й підтверджує: «так, я повернув JSON». У вигляді короткої схеми:

sequenceDiagram
    participant C as "Клієнт (Postman/застосунок)"
    participant S as "Сервер (API)"

    C->>S: "Запит із Accept: application/json"
    S->>C: "Відповідь 200 OK із Content-Type: application/json { ...JSON... }"

Тут важливо ще одне: статус-код і JSON — це різні рівні змісту. Якщо всередині JSON є поле status: "UP", це не HTTP-статус. HTTP-статус — це 200, 404, 500 тощо. Поле status — це просто дані, які ви поклали в body. Це як два різні «статуси» в житті: «я у стосунках» і «я отримав 200 OK» — не треба змішувати.

Ми це побачимо особливо яскраво в проєкті ReadLater Starter, коли перевірятимемо API через Postman: один і той самий JSON може приходити з різними HTTP-статусами (наприклад, помилка валідації та успішна відповідь — різні статуси), і це нормально. Тому вже зараз звикаємо: тіло відповіді — це дані, статус — це результат обробки на рівні протоколу, а заголовки — це метадані та договір про формат.

4. Обмеження JSON: «режим без сюрпризів»

Є парадоксальна думка: JSON хороший не тому, що він може все, а тому, що він не може надто багато. У нього невеликий набір типів, прості правила й доволі строгий синтаксис. І ця «скромність» перетворюється на передбачуваність.

Якби JSON дозволяв мільйон різних конструкцій, як мови програмування, то будь-який API був би лотереєю: «а як вони сьогодні закодували дату?», «а що означає ось ця конструкція?». Але JSON обмежений. Він дозволяє описувати структуру через об’єкти та масиви й зберігати значення як рядки, числа, булеві значення та null. Це означає, що клієнт і сервер зазвичай домовляються про правила один раз — а далі просто їх дотримуються.

У цьому є ще один бонус для бекенд-розробника: JSON добре підходить для «контрактного мислення». Ви бачите поля, бачите значення, бачите вкладеність — і можете вголос проговорити, що означає кожна частина. Це важливо в команді: коли ви обговорюєте API, вам потрібно говорити не «там якась фігня приходить», а «у відповіді є об’єкт верхнього рівня, всередині — масив items, а в кожного елемента є id і status».

Для нашого майбутнього ReadLater Starter це критично, тому що проєкт житиме у двох світах: спочатку ми отримуватимемо JSON із зовнішнього каталогу книг, а потім почнемо віддавати JSON із нашого локального API. Якщо формат даних «розмитий», ви не зможете стабільно писати код, який на нього спирається. Якщо формат фіксований і обмежений, він стає надійним фундаментом.

Саме тому сьогодні ми починаємо з простого: зрозуміти, чому JSON зручний і передбачуваний, і які базові правила роблять його «чесним форматом», а не «майже схожим на JSON рядком».

5. Строгий синтаксис JSON

На старті здається, що JSON — це щось на кшталт «ну я поставлю фігурні дужки й двокрапки, і все буде ок». Але JSON дуже не любить приблизності. Він не з тих форматів, які «здогадаються». Якщо JSON невалідний, парсер не зобов’язаний додумувати, що ви мали на увазі. Він просто скаже: «Ні, так не можна». І в певному сенсі це добре: менше сюрпризів, більше дисципліни.

Найчастіші базові правила, які ламають новачкам життя (і змушують вдивлятися в екран, як у матрицю), виглядають нескладно: рядки та імена полів мають бути в подвійних лапках, після останнього елемента не можна залишати кому, а сама структура має бути коректно закрита дужками. Так, це той рідкісний випадок, коли «одна зайва кома» справді може зламати вечір.

Подивіться на пару рядків. Вони виглядають схоже, але один із них не JSON:

// Валідний JSON: подвійні лапки й жодних «хвостових» ком
String validJson = "{ \"title\": \"Clean Code\" }";

// Невалідний JSON: одинарні лапки + зайва кома в кінці
String invalidJson = "{ 'title': 'Clean Code', }";

System.out.println(validJson);    // { "title": "Clean Code" }
System.out.println(invalidJson);  // { 'title': 'Clean Code', }

Другий варіант — типовий «майже JSON», який часто з’являється після копіпасту з якихось псевдоприкладів або з JavaScript-об’єкта (так-так, JavaScript-об’єкт і JSON — це не одне й те саме, але без заглиблення). Для API це важливо: клієнт може бути написаний будь-якою мовою, і він очікує валідний JSON, а не «ну приблизно схоже».

Ще один маленький нюанс: JSON валідний незалежно від того, наскільки він «гарний». Можна зробити ідеально відформатований, але невалідний JSON — і він не працюватиме. А можна зробити жахливо стиснутий, але валідний — і він працюватиме завжди. Краса — для людини, валідність — для машини. І, спойлер, саме валідність для машини зазвичай важливіша.

6. Пробіли та переноси в JSON

Коли ви починаєте працювати з JSON, дуже швидко з’являються два режими: «компактний» і «читабельний». Компактний трапляється в реальних відповідях сервісів (вони економлять трафік, а «красиво» вам і так покаже Postman). Читабельний ви самі робите в документації, у прикладах і під час дебагу. І ось важлива ідея: обидва варіанти описують одні й ті самі дані, поки форматування змінюється між JSON-токенами, а не всередині рядкових значень.

Якщо сказати зовсім по-людськи: пробіли й переноси рядків між JSON-токенами — це макіяж JSON. Вони роблять його привабливішим, але сутність не змінюють. Дані залишаються тими самими. А ось пробіл усередині самого рядка — уже частина значення.

Порівняймо два записи одного й того самого об’єкта:

// Компактна форма: мінімум символів, зміст той самий
String compact = "{\"title\":\"Clean Code\",\"author\":\"Robert C. Martin\"}";

// Читабельна форма: пробіли додані для людини, парсеру на них байдуже
String readable = "{ \"title\": \"Clean Code\", \"author\": \"Robert C. Martin\" }";

System.out.println(compact);  // {"title":"Clean Code","author":"Robert C. Martin"}
System.out.println(readable); // { "title": "Clean Code", "author": "Robert C. Martin" }

Обидва рядки валідні. У реальному API ви частіше отримаєте щось ближче до compact, а в інструментах (Postman, IDE) бачитимете красиво відформатовану версію. Це особливо корисно пам’ятати, коли ви «порівнюєте відповіді очима»: відмінності в пробілах не означають відмінностей у змісті.

І так, тут народжується корисна звичка: коли ви дивитеся на JSON, ви шукаєте не «красу», а структуру. Що на верхньому рівні: об’єкт чи масив? Які поля? Які значення? А вже потім — відступи, вирівнювання, переноси рядків. Postman та інші інструменти допоможуть вам із красою, а ви маєте навчитися бачити зміст.

Щоб пов’язати це з нашим проєктом, уявіть майбутню відповідь /health (ми її реалізуємо значно пізніше, але JSON-форма типова). Вона може бути «в один рядок» або красиво оформлена — контракт від цього не змінюється:

// У бойових відповідях часто приходить щось ближче до одного рядка
String healthCompact = "{\"status\":\"UP\",\"appName\":\"readlater-api\"}";

// А в прикладах і документації зручніше тримати читабельну форму
String healthPretty = """
    { "status": "UP", "appName": "readlater-api" }
    """;

System.out.println(healthCompact);        // {"status":"UP","appName":"readlater-api"}
System.out.println(healthPretty.strip()); // { "status": "UP", "appName": "readlater-api" }

Важливо лише одне: обидві версії мають бути валідними JSON і відповідати узгодженій структурі.

7. Типові помилки під час роботи з JSON

Помилка № 1: вважати JSON «будь-яким текстом у фігурних дужках».
Ця помилка зазвичай виглядає так: людина бачить { ... } і автоматично вирішує, що це JSON, а отже можна не хвилюватися. На практиці фігурні дужки — ще не формат. JSON вимагає строгих лапок, ком, двокрапок і коректного закриття структури. Хороша профілактика проста: якщо ви самі пишете JSON вручну (у прикладі, в документації, у Postman), ставтеся до нього як до коду — одна зайва кома справді ламає все.

Помилка № 2: забувати, що формат body підтверджується заголовком Content-Type.
Дуже легко піти в логіку «головне — що всередині body», а заголовки вважати другорядними. Але для HTTP-контракту заголовки — це частина договору. Якщо сервер віддає JSON, він зобов’язаний чесно написати Content-Type: application/json. Якщо клієнт надсилає JSON, він зобов’язаний поставити Content-Type у запиті. Інакше інша сторона або неправильно розбере body, або зробить вигляд, що нічого не розуміє (і, строго кажучи, матиме рацію).

Помилка № 3: плутати валідність JSON і його «красивість».
Новачки часто думають, що якщо JSON красиво відформатований, значить він коректний. Але форматування — це косметика. Невалідний JSON може бути ідеально вирівняний за відступами й усе одно не розпарситися. Валідний JSON може виглядати як каша в один рядок, і все одно бути ідеальним для машини. Тому правило таке: спочатку перевіряємо валідність і структуру, потім думаємо про читабельність.

Помилка № 4: використовувати одинарні лапки або залишати кому в кінці «за звичкою».
Це класична історія, особливо якщо людина до цього писала JavaScript або просто «якось так звикла». У JSON імена полів та рядки — у подвійних лапках, і кома після останнього елемента недопустима. JSON-парсер не зобов’язаний «зрозуміти, що ви мали на увазі» — він працює за правилами. Тут допомагає лише дисципліна й уважність, як під час введення пароля, тільки без права на «показати символи».

Помилка № 5: сприймати JSON як «просто текст» і намагатися витягати значення вручну через substring.
Навіть якщо здається, що «там же просто "title":"Clean Code" — я знайду це рядком», це швидко перетворюється на пастку. Будь-яка зміна форматування, порядок полів або вкладеність — і ваш «регулярний вираз» починає жити своїм життям. На рівні цієї лекції нам достатньо зафіксувати думку: JSON — це структуровані дані. Якщо вам потрібно дістати значення, значить у майбутньому ви розбиратимете його як структуру, а не як випадковий набір символів.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ