Как изменить значение в JSON с помощью jq, когда путь к ключу доступен только через select().

Вопрос или проблема

Начнем с примера вывода:

{
  "title": "Флэш (2014)",
  "alternateTitles": [
    {
      "title": "Флэш",
      "seasonNumber": -1
    }
  ],
  "sortTitle": "флэш 2014",
  "seasonCount": 7,
  "totalEpisodeCount": 154,
  "episodeCount": 142,
  "episodeFileCount": 0,
  "sizeOnDisk": 0,
  "status": "продолжается",
  "overview": "После удара молнии Барри Аллен просыпается из комы и обнаруживает, что обрёл суперскорость, став Флэшем и сражаясь с преступностью в Центральном городе.",
  "nextAiring": "2021-05-19T00:00:00Z",
  "previousAiring": "2021-05-12T00:00:00Z",
  "network": "The CW",
  "airTime": "20:00",
  "images": [
    {
      "coverType": "баннер",
      "url": "/MediaCover/37/banner.jpg?lastWrite=637552858236774582",
      "remoteUrl": "https://artworks.thetvdb.com/banners/graphical/279121-g7.jpg"
    },
    {
      "coverType": "постер",
      "url": "/MediaCover/37/poster.jpg?lastWrite=637552858237654584",
      "remoteUrl": "https://artworks.thetvdb.com/banners/posters/279121-5.jpg"
    },
    {
      "coverType": "фанарт",
      "url": "/MediaCover/37/fanart.jpg?lastWrite=637552858239814588",
      "remoteUrl": "https://artworks.thetvdb.com/banners/fanart/original/279121-9.jpg"
    }
  ],
  "seasons": [
    {
      "seasonNumber": 0,
      "monitored": false,
      "statistics": {
        "episodeFileCount": 0,
        "episodeCount": 0,
        "totalEpisodeCount": 8,
        "sizeOnDisk": 0,
        "percentOfEpisodes": 0
      }
    },
    {
      "seasonNumber": 1,
      "monitored": true,
      "statistics": {
        "previousAiring": "2015-05-20T00:00:00Z",
        "episodeFileCount": 0,
        "episodeCount": 0,
        "totalEpisodeCount": 23,
        "sizeOnDisk": 0,
        "percentOfEpisodes": 0
      }
    },
    {
      "seasonNumber": 2,
      "monitored": true,
      "statistics": {
        "previousAiring": "2016-05-25T00:00:00Z",
        "episodeFileCount": 0,
        "episodeCount": 0,
        "totalEpisodeCount": 23,
        "sizeOnDisk": 0,
        "percentOfEpisodes": 0
      }
    },
    {
      "seasonNumber": 3,
      "monitored": true,
      "statistics": {
        "previousAiring": "2017-05-24T00:00:00Z",
        "episodeFileCount": 0,
        "episodeCount": 0,
        "totalEpisodeCount": 23,
        "sizeOnDisk": 0,
        "percentOfEpisodes": 0
      }
    },
    {
      "seasonNumber": 4,
      "monitored": true,
      "statistics": {
        "previousAiring": "2018-05-23T00:00:00Z",
        "episodeFileCount": 0,
        "episodeCount": 0,
        "totalEpisodeCount": 23,
        "sizeOnDisk": 0,
        "percentOfEpisodes": 0
      }
    },
    {
      "seasonNumber": 5,
      "monitored": true,
      "statistics": {
        "previousAiring": "2019-05-15T00:00:00Z",
        "episodeFileCount": 0,
        "episodeCount": 0,
        "totalEpisodeCount": 22,
        "sizeOnDisk": 0,
        "percentOfEpisodes": 0
      }
    },
    {
      "seasonNumber": 6,
      "monitored": true,
      "statistics": {
        "previousAiring": "2020-05-13T00:00:00Z",
        "episodeFileCount": 0,
        "episodeCount": 0,
        "totalEpisodeCount": 19,
        "sizeOnDisk": 0,
        "percentOfEpisodes": 0
      }
    },
    {
      "seasonNumber": 7,
      "monitored": true,
      "statistics": {
        "nextAiring": "2021-05-19T00:00:00Z",
        "previousAiring": "2021-05-12T00:00:00Z",
        "episodeFileCount": 0,
        "episodeCount": 0,
        "totalEpisodeCount": 13,
        "sizeOnDisk": 0,
        "percentOfEpisodes": 0
      }
    }
  ],
  "year": 2014,
  "path": "/home/cas/plex-media/series/Флэш (2014)",
  "profileId": 6,
  "languageProfileId": 1,
  "seasonFolder": true,
  "monitored": false,
  "useSceneNumbering": false,
  "runtime": 42,
  "tvdbId": 279121,
  "tvRageId": 36939,
  "tvMazeId": 13,
  "firstAired": "2014-10-07T00:00:00Z",
  "lastInfoSync": "2021-05-16T03:08:01.309493Z",
  "seriesType": "standard",
  "cleanTitle": "флэш2014",
  "imdbId": "tt3107288",
  "titleSlug": "флэш-2014",
  "certification": "TV-PG",
  "genres": [
    "Экшн",
    "Приключения",
    "Драма",
    "Научная Фантастика"
  ],
  "tags": [],
  "added": "2021-04-29T09:37:02.970046Z",
  "ratings": {
    "votes": 6551,
    "value": 8.7
  },
  "qualityProfileId": 6,
  "id": 37
}

Теперь я хочу изменить значение .seasons | .[] | select(.seasonNumber==2).monitored на false. Я смог сделать это с помощью следующего:

# '.[] | select(.title==$TITLE)' используется для получения примера вывода
# '--argjson SEASON "$((season-1))"' будет 2 в этом случае (возвращая 3-й объект, который действительно является 2-м сезоном)

[api request] | jq -rM --arg TITLE "${info[$((series-1))]}" --arg SEASON "$((season-1))" '.[] | select(.title==$TITLE).seasons | .[] | select(.seasonNumber==$SEASON) | .monitored = false'

{
  "seasonNumber": 2,
  "monitored": false,
  "statistics": {
    "previousAiring": "2016-05-25T00:00:00Z",
    "episodeFileCount": 0,
    "episodeCount": 0,
    "totalEpisodeCount": 23,
    "sizeOnDisk": 0,
    "percentOfEpisodes": 0
  }
}

Однако я не хочу этот вывод. Я теперь получаю только сам объект. Я хочу получить полный пример вывода с изменённым значением. Я думаю, что проблемы в | после .seasons, но не знаю, как это исправить.

Моя цель – получить пример вывода, но с изменённым на false (и только для сезона, который я хочу)

ДОПОЛНЕНИЕ: следовало упомянуть, что я хочу получить вывод непосредственно в терминале и корректно. То есть не сохранять в файл в конце или в середине процесса (а затем использовать это в новой команде). Несколько команд и переменных допустимы.

ОТВЕТ МУРУ:

# для простоты я изменил значения --arg'ов на фактические значения
jq --arg TITLE "Флэш (2014)" --arg SEASON "2" '.[] | select(.title==$TITLE).seasons[] |= (select(.seasonNumber==$SEASON) |= (.monitored = false))'

Прежде всего, выводится [api request] вместо [api request] | jq -rM --arg TITLE "Флэш (2014)" '.[] | select(.title==$TITLE)'

Во-вторых, это… не меняет значение. Игнорируя полный вывод API, вместо этого он по-прежнему показывает "monitored": true, для "seasonNumber": 2,.

ДОПОЛНЕНИЕ2:

Я это сделал:

jq --arg TITLE "Флэш (2014)" --argjson SEASON "4" '.[] | select(.title==$TITLE) | .seasons[] |= (select(.seasonNumber==$SEASON) |= (.monitored = false))'

Разница:

  1. select(.title==$TITLE).seasons[] -> select(.title==$TITLE) | .seasons[]
  2. --arg SEASON "4" -> --argjson SEASON "4"

То, что вам нужно, по сути, это два обновления (обновить соответствующий объект сезона, а затем обновить массив сезонов с этим объектом). Итак:

.[] | select(.title==$TITLE).seasons[] |= (select(.seasonNumber==$SEASON) |= (.monitored = false))

Здесь внутреннее обновление (|=) обновляет selectированный сезон в исходном массиве, а внешнее |= обновляет массив результатом.

Ответ или решение

Чтобы изменить значение в JSON с использованием инструмента jq, особенно когда путь к ключу доступен только через select(), необходимо учесть несколько вещей. Рассмотрим, как можно изменить значение ключа .monitored у сезона 2 сериалa "The Flash (2014)" и при этом вернуть полностью обновленный объект JSON.

Исходный JSON содержит массив сезонов, где каждый сезон представлен объектом. Мы хотим изменить значение ключа monitored для сезона с seasonNumber равным 2.

Вот общее решение:

jq --arg TITLE "The Flash (2014)" --argjson SEASON 2 \
'(.[] | select(.title==$TITLE).seasons[] | select(.seasonNumber==$SEASON).monitored) = false'

Разбор команды:

  1. --arg TITLE "The Flash (2014)" – мы задаем переменную, чтобы выбрать нужный сериал по его названию.
  2. --argjson SEASON 2 – здесь мы задаем номер сезона, который хотим обновить. Используется --argjson, поскольку мы передаем числовое значение.
  3. (.[] | select(.title==$TITLE).seasons[] | select(.seasonNumber==$SEASON).monitored) = false – эта часть команды выполняет следующие действия:
    • Выбирает сериал по заголовку.
    • Достает массив сезонов.
    • Среди сезонов выбирает сезон с нужным номером.
    • Меняет значение monitored на false.

После выполнения этой команды мы получим полностью обновленный объект исходного JSON с измененным значением для ключа monitored для указанного сезона.

Пример использования

Предположим, что вы получили данные из API и сохранили их в переменной api_response. Тогда вы можете применить jq следующим образом:

echo "$api_response" | jq --arg TITLE "The Flash (2014)" --argjson SEASON 2 '(.[] | select(.title==$TITLE).seasons[] | select(.seasonNumber==$SEASON).monitored) = false'

Этот вызов выведет обновленный JSON, содержащий все данные, в том числе изменения, которые вы внесли.

Заключение

Таким образом, используя jq, можно легко обновлять значения в JSON-структурах, применяя фильтры select() для выборки нужных элементов. Это позволяет эффективно обновлять сложные структуры данных, не теряя при этом целостности оригинального JSON.

Оцените материал
Добавить комментарий

Капча загружается...