обновить часть JSON с помощью функции захвата в jq - PullRequest
0 голосов
/ 03 августа 2020

Скажем, у меня есть JSON, который содержит недостающие данные, которые можно вычислить из уже имеющихся данных с помощью регулярного выражения.

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

[
  {
    "id": "a100",
    "data": [
      {
        "something": null
      }
    ]
  },
  {
    "id": "a101",
    "data": [
      {
        "something": null
      }
    ]
  },
  {
    "id": "b100",
    "data": [
      {
        "something": null
      }
    ]
  }
]

Предположение: для простоты / В этом примере я использую массив как элемент верхнего уровня. Представьте себе намного больший JSON с большей вложенностью данных, находящихся в этом массиве, ie. это не может быть решено добавлением создания массива.

Скажем, нам нужно вычислить something из id, где something должно быть id с добавлением первых двух символов в конец:

[
  {
    "id": "a100",
    "data": [
      {
        "something": "a100a1"
      }
    ]
  },
  {
    "id": "a101",
    "data": [
      {
        "something": "a101a1"
      }
    ]
  },
  {
    "id": "b100",
    "data": [
      {
        "something": "b100b1"
      }
    ]
  }
]

Я смог придумать:

jq '.[] 
| . as $env 
| .data[].something=($env.id | capture("(?<cid>[a-z0-9]{2})(?<rest>.*)") | .cid+.rest+.cid)' < test.json

, но это «решение» опускает все, что связано с объявленной переменной, которая в этом упрощенном примере является массивом верхнего уровня. Итак, вопрос: поскольку мы go глубже в структуре json, как сохранить некоторые значения вдоль пути узла, чтобы мы могли использовать эти переменные для обновления какой-то части структуры, сохраняя при этом остальные нетронутыми?

РЕДАКТИРОВАТЬ: приведенный выше пример был неверным, потому что его можно легко решить без использования функции capture, о которой я спрашивал. Приведу другой пример. Проблема в том, что я не знаю, как использовать |= для обновления части json, если capture возвращает json-data, где я потеряю данные, необходимые для обновления, недостаточно понимая, зачем объявлять переменную блокирует меня от использования |= иногда et c et c. Ie. Я понятия не имею, как его использовать, несмотря на многие попытки и поиск в Google.

{
  "arr": [
    {
      "a": {
        "id": "a100",
        "b": {
          "c": [
            {
              "data": [
                {
                  "something": null
                }
              ]
            }
          ]
        }
      }
    },
    {
      "a": {
        "id": "bPleaseUseRegex100",
        "b": {
          "c": [
            {
              "data": [
                {
                  "something": null
                }
              ]
            }
          ]
        }
      }
    }
  ]
}

для создания:

{
  "arr": [
    {
      "a": {
        "id": "a100",
        "b": {
          "c": [
            {
              "data": [
                {
                  "something": "a100a"
                }
              ]
            }
          ]
        }
      }
    },
    {
      "a": {
        "id": "bPleaseUseRegex100",
        "b": {
          "c": [
            {
              "data": [
                {
                  "something": "bPleaseUseRegex100bPleaseUseRegex"
                }
              ]
            }
          ]
        }
      }
    }
  ]
}

о правиле регулярного выражения: все от начала id до первого десятичное число необходимо добавить к id, чтобы получить something. Tbh Меня не волнует, используете ли вы вместо него .*, просто используйте функцию capture, чтобы увидеть, как это работает.

Ответы [ 2 ]

1 голос
/ 03 августа 2020

На самом деле вы не так уж и далеко, за исключением

  1. Вы хотите использовать map, чтобы окончательный результат оставался массивом.
  2. Вы хотите использовать |= вместо =, чтобы изменить подпуть ., при этом возвращая ..
  3. На самом деле вам не нужно регулярное выражение, чтобы захватить два символа с начала строки.

Итак, это должно работать:

jq 'map((.id + .id[0:2]) as $newid | .data[].something |= $newid)`
0 голосов
/ 15 августа 2020

Большое спасибо @hobbs, который оказал большую помощь в решении этой проблемы.

Что касается обновленного вопроса, касающегося использования захвата, решение выглядит следующим образом:

jq '.arr |= map(.a.id as $id | .a.b.c[].data[].something |= ($id | capture("(?<a>[a-zA-z]*)(?<b>.*)") | (.a+.b+.a)  ) ) ' < a.json > c.json

Итак, что мне не хватало, и что требовалось для перехода от ответа hobbs к использованию функции capture, было просто парой или (). Позвольте мне попытаться объяснить это (я далек от знаний Хоббса, но давайте попробуем).

вы определяете массив, который хотите обновить, в данном случае .arr. Вам необходимо обновить его, иначе в результате вы не получите исходных данных. Вы обновляете массив arr, используя map, который в этом случае применяет некоторое преобразование и возвращает новый массив; в этом примере всего 2 вещи: присвоение переменной, которое не изменяет контекст, и другой оператор |=, поэтому первый |= обновляет .arr с обновленным self. О преобразовании внутри map: сначала мы объявляем переменную $id, которая не меняет контекст. Затем мы говорим, что в текущем контексте мы хотели бы обновить .a.b.c[].data[].something новым значением. Это новое значение может быть буквальным или чем-то еще, что не меняет контекст, и, поскольку это делает захват, нам просто нужно использовать скобки (). Итак, здесь в скобках мы расширяем переменную $id, захватываем регулярное выражение и из нового контекста, созданного capture, мы создаем новое значение для something, используя конкатенацию строк (.a+.b+.a).

(отказ от ответственности: I ' m jq ученик, поэтому некоторые термины в объяснении могут быть нестабильными, но предоставленная команда должна работать)

...