Объединение нескольких блоков данных JSON в единый объект

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

Я использую API (от SyncroMSP), который возвращает пагинированные данные в формате JSON. Я могу получить количество страниц, и я могу получить данные с помощью инструмента, такого как curl. Каждый кусок данных является валидным JSON, но содержит только подмножество всех данных, которые мне нужны.

С помощью jq или другим способом, как я могу объединить элементы tickets[] этих пагинированных данных обратно в один документ JSON?

Вот три примера кусочков. Массивы tickets[] сильно отредактированы для этого вопроса и на самом деле содержат до 25 записей, и каждая запись билета содержит много других элементов, включая как минимум несколько массивов.

Пример блока JSON 1 (part_1.json)

{
  "tickets": [
    {
      "number": 4445,
      "subject": "Ваш почтовый ящик почти заполнен"
    },
    {
      "number": 4444,
      "subject": "Не удается подключиться к VPN"
    }
  ],
  "meta": {
      "total_pages": 3,
      "page": 1
  }
}

Пример блока JSON 2 (part_2.json)

{
  "tickets": [
    {
      "number": 4395,
      "subject": "Проблема с Trados Studio"
    },
    {
      "number": 4394,
      "subject": "Ежедневный отчет о резервном копировании (без ошибок)"
    }
  ],
  "meta": {
      "total_pages": 3,
      "page": 1
  }
}

Пример блока JSON 3 (part_3.json)

{
  "tickets": [
    {
      "number": 4341,
      "subject": "Ежедневный отчет о резервном копировании (без ошибок)"
    },
    {
      "number": 4340,
      "subject": "Обновления Windows на ВМ"
    }
  ],
  "meta": {
      "total_pages": 3,
      "page": 1
  }
}

В данном случае ожидаемый результат будет примерно таким:

{
  "tickets": [
    {
      "number": 4445,
      "subject": "Ваш почтовый ящик почти заполнен"
    },
    {
      "number": 4444,
      "subject": "Не удается подключиться к VPN"
    },
    {
      "number": 4395,
      "subject": "Проблема с Trados Studio"
    },
    {
      "number": 4394,
      "subject": "Ежедневный отчет о резервном копировании (без ошибок)"
    },
    {
      "number": 4341,
      "subject": "Ежедневный отчет о резервном копировании (без ошибок)"
    },
    {
      "number": 4340,
      "subject": "Обновления Windows на ВМ"
    }
  ]
}

Вывод также может включать хеш meta, так как я просто проигнорирую его, и не имеет значения, какое значение meta.page было перенесено.

Вы можете предположить, что tickets[].number уникален и что вам не нужно сохранять никакую сортировку на уровне tickets[]. В реальных данных достаточно сложности, и я не хочу объявлять всю структуру JSON в любом итоговом коде.

Это моя текущая попытка, но я не очень силен в jq. Есть ли лучший способ – например, избежать вызова jq дважды или сделать так, чтобы не нужно было указывать имя верхнего уровня массива (tickets)?

cat part_{1,2,3}.json | jq '.tickets[]' | jq -n '{ tickets:[ inputs ] }'

Вы можете использовать map | add, чтобы объединить все билеты в один массив:

jq -s '{tickets: map(.tickets) | add}' part_{1..3}.json

Или с параметризацией tickets,

key=$(jq -r 'to_entries[] | select(.value | type == "array") | .key' part_1.json)
jq -s --arg key "$key" '{ ($key): map(.[$key]) | add }' part_[123].json

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

Для слияния нескольких блоков JSON, полученных из пагинированного API, в единый документ JSON, вы можете воспользоваться утилитой jq, которая предназначена для обработки данных в формате JSON. В этом ответе мы рассмотрим, как сформировать объединённый массивTicket` из нескольких файлов, сохраняя при этом структуру данных и избегая дублирования.

Шаги по объединению данных

1. Подготовка файлов

Убедитесь, что у вас есть файлы, содержащие различные блоки JSON, например:

  • part_1.json
  • part_2.json
  • part_3.json

2. Использование jq для объединения

Чтобы объединить массив tickets из различных файлов в один массив, можно использовать следующую команду:

jq -s '{tickets: map(.tickets) | add}' part_*.json

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

  • -s: Опция (или флаг), позволяющая jq читать входные данные в виде массива JSON. Это значит, что все переданные файлы будут объединены в один большой массив.
  • map(.tickets): Эта часть команды извлекает массив tickets из каждого объекта JSON, возвращая новый массив, состоящий только из массивов tickets.
  • add: Функция, которая объединяет все массивы ‘tickets’ в один общий массив.

В результате выполнения команды вы получите объединённый массив tickets, содержащий все элементы из всех указанных файлов.

Параметризация для большей гибкости

Если вы хотите сделать ваш подход более универсальным, чтобы не зависеть от жестко заданного имени ключа (например, tickets), вы можете выполнить следующие шаги:

  1. Извлекаем ключи, которые являются массивами:

    key=$(jq -r 'to_entries[] | select(.value | type == "array") | .key' part_1.json)
  2. Используем этот ключ в jq для объединения:

    jq -s --arg key "$key" '{ ($key): map(.[$key]) | add }' part_*.json

Этот способ позволяет вам динамически работать с разными структурами JSON, не меняя сами команды.

Подводя итоги

В этой инструкции мы рассмотрели, как использовать jq для слияния разделённых массивов tickets из нескольких файлов JSON в единую сущность. Выбор правильного подхода позволяет вам не только упростить обработку данных, но и избежать ошибок и дублирования, что может значительно повлиять на качество анализа данных. Важно помнить о гибкости вашей команды, особенно если структура JSON может варьироваться.

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

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