JavaRush /Курсы /Java Server /Чтение вложенного JSON

Чтение вложенного JSON

Java Server
10 уровень , 3 лекция
Открыта

1. Вложенный JSON — нормальная форма

Если вы привыкли к учебным примерам из двух полей, то реальный API-ответ может выглядеть как матрёшка: объект, внутри массив, внутри ещё объект, а рядом “служебные” поля. Это не из вредности авторов API, а из попытки передать данные вместе с контекстом: количеством результатов, страницей, статусом операции.

Начнём с простой человеческой причины: данные в мире редко плоские. Книга имеет название и год, но ещё у неё есть авторы (и это уже список), иногда — несколько идентификаторов (внешний ID, внутренний ID), иногда — вложенный блок “publisher” или “links”. Когда API возвращает это всё одним ответом, он неизбежно использует вложенность.

Вторая причина практичная: API часто хочет вернуть не только «список объектов», но и подсказку «сколько их вообще» (например, count), «какой запрос вы сделали» (query), «время обработки» (tookMs). Получается объект-обёртка, где есть и основные данные, и метаданные рядом. Это как посылка: внутри — ваш заказ, а снаружи — наклейка “хрупкое”, “вес”, “номер отслеживания”. Содержимое важно, но и наклейка помогает понять, что происходит.

И да, вложенность почти всегда появляется в тех местах, где есть отношения: “результаты поиска → каждый результат → у результата авторы → у автора имя”. В будущем, когда вы будете смотреть на реальные ответы внешнего каталога (через Postman), вы увидите эту матрёшку постоянно. Наша задача сейчас — научиться её спокойно раскручивать, а не пытаться съесть целиком.

2. Верхний уровень документа

Вложенный JSON проще всего читать, если вы всегда начинаете с одного и того же вопроса: что у нас на самом верху — объект или массив? Это занимает две секунды, но экономит десять минут паники. Первый символ (после пробелов) почти всегда сразу отвечает: { означает объект, [ — массив.

Это супер-простое правило, но оно реально работает как «ремень безопасности». Потому что если вы ошиблись на верхнем уровне, вы дальше будете неправильно интерпретировать всё остальное. Например, вы ожидаете поле count, а у вас сверху массив — и вы будете его искать в каждой строке, как “Где тут кнопка ‘Сохранить’?” в микроволновке.

Небольшая табличка, чтобы закрепить смысл «верхнего символа»:

Верхний уровень Как выглядит Что это означает Типичный смысл в API
Объект
{ ... }
Набор именованных полей Ответ «про одну сущность» или «обёртка вокруг списка + метаданные»
Массив
[ ... ]
Список значений Ответ «только список, без метаданных» (встречается, но реже)

Внутри API чаще встречается именно объект на верхнем уровне, потому что он даёт гибкость: можно вернуть items, count, requestId и не ломать контракт, если добавятся новые поля. Массив на верхнем уровне тоже валиден, но он менее удобен, если вы захотите рядом добавить ещё что-то кроме элементов списка.

Если вам хочется «почувствовать» это правило на Java (без парсинга, просто как привычку смотреть на форму), можно сделать совсем маленький пример. Он не разбирает JSON — он просто показывает, что верхний уровень читается даже по первому символу:

// Пример JSON-строки (именно строки, без парсинга).
String json = """
        { "items": [ { "id": 1, "title": "Clean Code" } ], "count": 1 }
        """;

// Убираем пробелы/переносы по краям, чтобы первый символ точно был { или [.
char topLevel = json.strip().charAt(0);

System.out.println(topLevel); // Ожидаем '{' — значит, на верхнем уровне объект.

Здесь strip() убирает пробелы и переносы по краям, а charAt(0) берёт первый «настоящий» символ. Это очень простая штука, но она тренирует правильный рефлекс: сначала форма, потом детали.

3. Данные и контекст

Когда верхний уровень — объект, мозг новичка часто пытается читать его слева направо, как текст. Но JSON — не роман, а структура. Поэтому полезно сразу разделить поля на две группы: те, где лежит основная “полезная нагрузка” (обычно массив или большой объект), и те, что описывают эту нагрузку: count, page, requestId.

Представьте, что вы открыли JSON и видите пять полей. Вопрос не «какое поле первое», а «какое поле главное». Обычно главное поле выглядит как крупный контейнер, то есть объект {...} или массив [...]. А рядом с ним лежат простые значения: числа, строки, boolean.

Чтобы не утонуть в структуре, в паре примеров я подпишу части ответа комментариями. Это уже jsonc, а не валидный JSON: в настоящем JSON комментариев нет, но для чтения формы так понятнее.

Например, вот типичная форма ответа поиска (упрощённая):

{
  // Основные данные: список результатов
  "results": [
    { "title": "Clean Code", "year": 2008 },
    { "title": "Refactoring", "year": 1999 }
  ],
  // Метаданные: сколько результатов вернулось
  "count": 2
}

Здесь results — это основной массив данных, а count — метаданные о нём. И вам психологически проще читать так: «в ответе есть results; results — это список; у списка 2 элемента; отдельно написано count=2». Если вы читаете в другой последовательности, вы сначала зацепитесь за count, потом прыгнете в массив, потом потеряете, где вы были.

Есть ещё один типичный источник путаницы, который важно проговорить прямо сейчас, пока мы не ушли глубже: поле status внутри JSON не равно HTTP-статусу ответа. Это разные уровни мира.

Сравните:

Где мы находимся Что такое status Пример
HTTP-уровень Код ответа сервера 200 OK,
404 Not Found
JSON-уровень (в body) Просто поле в данных
"status": "UP"
или
"status": "FINISHED"

Например, ответ /health в нашем будущем локальном API может выглядеть так:

{
  // Это поле в JSON body, а не HTTP-статус ответа
  "status": "UP",
  "appName": "readlater-api"
}

И при этом HTTP-статус будет 200 OK. Поле "status": "UP" — это часть контракта body, а не транспортная часть HTTP. Это похоже на ситуацию «в письме написано “всё хорошо”, но конверт всё равно может быть порван». Конверт — это HTTP, письмо — это JSON.

4. Вложенность как путь

Вложенность пугает, когда вы пытаетесь держать весь документ в голове одновременно. Чит-код здесь — мыслить не “всем JSON сразу”, а конкретным маршрутом до нужного значения. Такой маршрут можно записывать словами (“results → первый элемент → authors → первый автор”) или в более программной форме вроде results[0].authors[0].

Сама идея очень простая: если JSON — это структура, то до любого значения можно добраться «по лестнице». Мы идём сверху вниз, выбирая либо поле объекта, либо элемент массива.

Давайте возьмём небольшой вложенный пример, где есть и массив, и массив внутри объекта:

{
  // Главный контейнер данных: массив результатов
  "results": [
    {
      "title": "Clean Code",
      "authors": ["Robert C. Martin"],
      "year": 2008
    }
  ],
  // Метаданные: общее количество элементов
  "count": 1
}

Теперь запишем несколько «путей» и что они означают. Я буду использовать понятную программистскую запись: . для поля объекта и [0] для элемента массива.

Путь Как читать по-человечески Что получаем
count поле count на верхнем уровне 1
results поле results на верхнем уровне массив из 1 элемента
results[0] первый элемент массива results объект книги
results[0].title поле title внутри первой книги "Clean Code"
results[0].authors[0] первый автор первой книги "Robert C. Martin"

Важно: индекс [0] — это «первый элемент», потому что в Java, как вы помните, индексация с нуля. Это тот самый классический источник ошибок «почему у меня второй элемент стал первым». В чтении JSON индексация не обязана быть нулевой (вы же не обязаны записывать путь именно так), но для будущего кода полезно мыслить сразу в Java-координатах.

Ещё один важный момент: этот «путь» — не библиотека и не стандарт, мы не подключаем никаких JSONPath-штук и не делаем магию. Это просто удобный язык, чтобы ваш мозг (и ваши заметки) не превращались в кашу. Когда вы позже будете читать реальные ответы в Postman, такая запись очень помогает: вы можете прямо себе написать «мне нужно значение из results[0].title» — и сразу понятно, где его искать.

5. Массив объектов: элемент как шаблон

Самая частая конструкция в API — массив объектов: список книг, список пользователей, список элементов списка чтения. Ошибка новичка — пытаться глазами пробежать сразу по всему массиву и утонуть в повторяющихся кусках. Гораздо спокойнее сначала вытащить один элемент и понять его форму: какие поля есть и какие из них вложенные.

Почему это работает? Потому что массив объектов почти всегда означает «много однотипных сущностей». То есть каждый элемент массива — это «книга», и у каждой книги примерно одинаковый набор полей (хотя некоторые могут быть отсутствующими или null — но смысл этого мы разберём в следующей лекции, без спойлеров).

Посмотрите на массив — и мысленно скажите себе: «Окей, это список. Я беру первый элемент как пример. Понимаю, что внутри. Потом уже думаю про второй, десятый и т.д.»

Вот пример чуть сложнее, с вложенным объектом и вложенным массивом:

{
  "results": [
    {
      "title": "Clean Code",
      "meta": { "language": "en", "pages": 464 },
      "authors": ["Robert C. Martin"]
    }
  ],
  "count": 1
}

Если вы сначала поймёте форму одного элемента results[0], дальше всё становится предсказуемо. Вы уже знаете, что где-то там есть title, meta.language, meta.pages, authors[0]. А когда увидите второй элемент массива, вы будете проверять не «всё подряд», а «а он такой же формы?».

Кстати, это очень близко к тому, как мы потом будем писать код в клиентской фазе проекта: сначала понимаем структуру данных, потом строим обработку. Но сегодня мы остаёмся на уровне чтения и понимания, без сериализации и без Java-объектов.

6. Пример ReadLater: ответ списка чтения

Давайте привяжем технику к нашему сквозному проекту ReadLater Starter. Позже у нас появится локальный API, который будет отдавать список книг для чтения. Ответ будет не просто “голый массив”, а объект-обёртка с полем items (сам список) и count (сколько всего элементов). Это отличный учебный пример вложенности без лишней экзотики.

Представим будущий ответ эндпоинта GET /api/v1/reading-list (мы пока не реализуем сервер, мы просто учимся читать форму данных):

{
  // Основные данные: список элементов reading list
  "items": [
    {
      "id": 1,
      "title": "Clean Code",
      "author": "Robert C. Martin",
      "status": "PLANNED",
      "externalId": "OL12345M"
    }
  ],
  // Метаданные: сколько всего элементов в items
  "count": 1
}

Теперь читаем его спокойно, по шагам, как по инструкции к шкафу из IKEA (только без лишних винтов, надеюсь):

Сначала верхний уровень: это объект {...}. Значит, на верхнем уровне мы ищем поля. Видим два поля: items и count. Уже на этом этапе можно сказать: «ага, это обёртка; items — вероятно основные данные; count — метаданные».

Дальше поле items: оно начинается с [ — значит, это массив. Следовательно, внутри items лежит список элементов. Мы смотрим на первый элемент items[0] и видим {...} — объект. Значит, каждый элемент списка — это объект одного элемента reading list. И у него есть поля id, title, author, status, externalId.

И вот вам готовые «пути» до значений:

Что хотим узнать Путь Пример значения
Сколько всего элементов в ответе count 1
Название первой книги items[0].title
"Clean Code"
Статус первой книги items[0].status
"PLANNED"
Внешний идентификатор items[0].externalId
"OL12345M"

Если вы хотите связать это с кодом (опять же: без парсинга, просто как привычка держать «пример ответа» рядом), удобно хранить такие JSON-фрагменты в Java как text block. Это нормально для учебной стадии, когда вы учитесь глазами читать структуру:

// В учебных примерах удобно держать "пример ответа" прямо строкой.
String readingListJson = """
        {
          "items": [
            { "id": 1, "title": "Clean Code", "status": "PLANNED" }
          ],
          "count": 1
        }
        """;

// Примитивная проверка "в тексте вообще есть нужное поле" (это НЕ парсинг JSON).
System.out.println(readingListJson.contains("\"items\"")); // true

Последняя строка не «разбирает JSON», она просто показывает: это обычный текст, и вы можете делать с ним самые базовые вещи. Позже у нас появится нормальная обработка JSON в коде, но сегодня важно именно понимание структуры.

7. JSON как дерево

Человеческие глаза любят структуру, а не бесконечные кавычки и запятые. Поэтому, когда JSON становится вложенным, полезно на минуту забыть про “текст” и нарисовать дерево: корень документа, его дочерние поля, где массивы, где объекты, и что повторяется. Это похоже на карту метро: по ней проще понять, где вы находитесь.

Возьмём тот же пример reading list и изобразим его как дерево. Это не «формальный стандарт», а просто очень полезная шпаргалка:

// Дерево JSON: в скобках указан тип узла (object/array/string/number).
root (object)
├─ items (array)
│  └─ [0] (object)
│     ├─ id (number)
│     ├─ title (string)
│     ├─ author (string)
│     ├─ status (string)
│     └─ externalId (string)
└─ count (number)

Когда вы видите такую картинку, сразу становится ясно несколько вещей. Во-первых, items — повторяющийся блок, и его структура — структура одного элемента. Во-вторых, count не «где-то там рядом», а на верхнем уровне. В‑третьих, если вы будете описывать контракт словами, вы можете сказать: «ответ — объект с полями items и count; items — массив объектов; у объекта есть id/title/author/status/externalId».

Если хочется ещё более «визуально», можно представить то же самое как поток (корень → контейнер → элементы). Mermaid-схема тут работает как наглядный способ не перепутать уровни:

flowchart TD
    %% Визуализация уровней: root -> items -> элемент массива; count лежит на верхнем уровне.
    A["root: object"] --> B["items: array"]
    B --> C["[0]: object"]
    C --> D["title: string"]
    C --> E["status: string"]
    A --> F["count: number"]

Не бойтесь рисовать такие схемы даже на бумаге. Это не признак слабости, это признак того, что вы не пытаетесь быть «компилятором на ножках». Компилятор пусть остаётся компилятором, а мы будем инженерами: сначала понимаем структуру, потом пишем код.

8. Типичные ошибки при чтении вложенного JSON

Ошибка №1: начинать чтение «с середины», потому что глаз зацепился за знакомое слово.
Очень хочется увидеть где-то внутри "title" и сразу начать «а, значит это книга». Но вы теряете контекст: где именно этот title живёт — на верхнем уровне, внутри book, внутри results[0]? Правильная привычка — сначала посмотреть верхний уровень {} или [], затем определить главный контейнер (items/results), и только потом идти к конкретным полям по пути.

Ошибка №2: путать объект и массив, потому что оба выглядят как «какая-то конструкция со скобками».
Это смешно ровно до первого реального ответа, где у вас items: [], и вы внезапно пытаетесь искать поле items.title. У объекта есть поля по именам, у массива есть элементы по индексу. Если в голове не переключить режим, вы будете «доставать поле из списка», а список будет смотреть на вас молча и осуждающе.

Ошибка №3: читать объект слева направо и придавать значение порядку полей.
В JSON-объекте порядок полей не должен быть смысловым. Сегодня API прислал count после items, завтра — перед items, послезавтра добавил requestId в середину. Если вы «привязываетесь глазами» к порядку, вы будете чувствовать, что контракт «постоянно меняется», хотя меняется только форматирование. Держитесь за имена полей и структуру контейнеров, а не за позицию в тексте.

Ошибка №4: смешивать поля верхнего уровня и поля элементов массива.
Классический случай: есть count рядом с items, а в каждом item ещё есть id. Новичок начинает воспринимать count как «ещё одно поле элемента», хотя оно относится ко всему ответу. Тут помогает дерево: count живёт на root-уровне, а id — на уровне items[0]. Важно проговаривать это вслух хотя бы первые пару раз.

Ошибка №5: путать status в JSON-body с HTTP status ответа.
Если вы видите { "status": "UP" }, это не значит, что сервер вернул HTTP 200 (хотя обычно вернул). И наоборот: сервер может вернуть HTTP 500, а в body будет вообще другая структура (или не будет body). Держите в голове «два слоя»: транспортный (HTTP status/headers) и прикладной (JSON поля).

1
Задача
Java Server, 10 уровень, 3 лекция
Недоступна
Карта путей для вложенного JSON
Карта путей для вложенного JSON
1
Задача
Java Server, 10 уровень, 3 лекция
Недоступна
HTTP status и поле status внутри JSON body
HTTP status и поле status внутри JSON body
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ