Добавление к подходящим элементам произвольного глубинного массива

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

Я пытаюсь изменить спецификацию OpenAPI, сделав все параметры допускающими значение null. Определение параметра выглядит следующим образом:

{
    "name": "foo",
    "required": true,
    "type": "string"
}

Они содержатся в массиве parameters, который может находиться в любом месте документа. Мне нужно добавить "x-nullable": true к любому параметру, содержащему свойство type.

Пример данных:

{
    "parameters": [
        {"notype": "x"},
        {"type": "x"}
    ],
    "foo": {
        "parameters": [
            {"type": "y"}
        ]
    },
    "bar": {
        "baz": {
            "parameters": [
                {"type": "z"}
            ]
        }
    }
}

Желаемый результат:

{
    "parameters": [
        {"notype": "x"},
        {
            "type": "x",
            "x-nullable": true
        }
    ],
    "foo": {
        "parameters": [
            {
                "type": "y",
                "x-nullable": true
            }
        ]
    },
    "bar": {
        "baz": {
            "parameters": [
                {
                    "type": "z",
                    "x-nullable": true
                }
            ]
        }
    }
}

Самое близкое, чего мне удалось достичь, это это:

.. | (.parameters[] | select(.type)) += {"x-nullable":true}

Это успешно изменяет один из элементов в моем тестовом документе, но результаты непоследовательны и кажутся зависящими от структуры, выбранной для образца данных.

Следующее выражение jq применяет обновление к значениям всех ключей parameters, найденных в документе. Предполагается, что ключ parameters всегда относится к массиву. Обновление назначает true ключу x-nullable всех элементов массива с ключом type.

Рекурсия выполняется с помощью walk() (применяет выражение к каждому значению в документе рекурсивно), а не с помощью оператора рекурсивного спуска .. (извлекает каждое значение документа рекурсивно).

walk(
    if type == "object" and has("parameters")
    then
        .parameters |= map(select(has("type"))."x-nullable" = true)
    else
        .
    end
)

Безопаснее использовать select(has("type")) для выбора объектов, имеющих поле type, поскольку использование select(.type) пропустило бы объекты, у которых значение type было false или null.

Тестирование на образце документа в вопросе:

$ jq 'walk(if type == "object" and has("parameters") then .parameters |= map(select(has("type"))."x-nullable" = true) else . end)' file
{
  "parameters": [
    {
      "notype": "x"
    },
    {
      "type": "x",
      "x-nullable": true
    }
  ],
  "foo": {
    "parameters": [
      {
        "type": "y",
        "x-nullable": true
      }
    ]
  },
  "bar": {
    "baz": {
      "parameters": [
        {
          "type": "z",
          "x-nullable": true
        }
      ]
    }
  }
}

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

Изменение спецификации OpenAPI, чтобы все параметры стали допускающими null, может быть сложной задачей из-за глубины и структуры JSON-документа. Ваша цель — добавить "x-nullable": true ко всем параметрам, которые содержат свойство type. Это изменение должно быть выполнено для параметров в parameters массива, который может находиться в любом месте документа. Чтобы решить эту проблему эффективно, вам потребуется использовать утилиту для работы с JSON, такую как jq, которая позволяет манипулировать данными на основе соответствующих условий.

Теория

jq — это мощный инструмент командной строки, используемый для обработки и фильтрации данных JSON. Его основной задачей является выполнение операций по спецификации JSON и выборки данных на основе указанных условий. В данном контексте, наиболее подходящий подход будет включать использование walk() функции, которая применяется ко всем значениям в документе рекурсивно. Это позволяет модифицировать все необходимые параметры, вне зависимости от их положения в JSON-структуре.

Функция walk() применяет определенное выражение к каждому значению в документе, проверяя, является ли текущее значение объектом, содержащим массив parameters. При нахождении такого массива, каждое из его значений проверяется на наличие ключа type, и если такой ключ присутствует, добавляется "x-nullable": true.

Пример

Рассмотрим следующий пример JSON-документа:

{
    "parameters": [
        {"notype": "x"},
        {"type": "x"}
    ],
    "foo": {
        "parameters": [
            {"type": "y"}
        ]
    },
    "bar": {
        "baz": {
            "parameters": [
                {"type": "z"}
            ]
        }
    }
}

Ожидается, что в результате применения jq выражения все параметрические объекты с наличием ключа type будут модифицированы следующим образом:

{
    "parameters": [
        {"notype": "x"},
        {
            "type": "x",
            "x-nullable": true
        }
    ],
    "foo": {
        "parameters": [
            {
                "type": "y",
                "x-nullable": true
            }
        ]
    },
    "bar": {
        "baz": {
            "parameters": [
                {
                    "type": "z",
                    "x-nullable": true
                }
            ]
        }
    }
}

Применение

Для достижения этого результата, можно использовать следующее jq выражение:

jq 'walk(if type == "object" and has("parameters") then .parameters |= map(select(has("type"))."x-nullable" = true) else . end)' файл

Объяснение выражения:

  1. walk(): Это выражение рекурсивно проходит через все элементы JSON. Оно проверяет каждый элемент на соответствие условиям.

  2. if type == "object" and has("parameters"): Проверяет, является ли текущий элемент объектом и содержит ли он ключ parameters.

  3. .parameters |= map(…): Применяет функцию map() к массиву parameters, чтобы изменять каждый элемент в массиве.

  4. select(has("type")): Выбирает только те объекты внутри массива parameters, которые имеют ключ type.

  5. "x-nullable" = true: Устанавливает свойство "x-nullable": true для каждого объекта, где присутствует ключ type.

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

Итог

Изменение JSON-документа, как описано выше, позволяет легко корректировать структуры данных в соответствии с требованиями OpenAPI, обеспечивая гибкость и точность в обработке данных. Это решение не только делает параметры допускающими null, но и поддерживает структурную целостность и допустимость JSON-документа, что особенно важно для сложных конфигураций API.

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

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