Распечатать элемент ввода, даже если capture() не соответствует выражению

Я пытаюсь извлечь информацию об определенных конкретных зависимостях из выходных данных конечной точки /v1/project DTrack.

API возвращает данные в формате:

[{
    "name": "project name",
    "classifier": "LIBRARY | APPLICATION",
    "directDependencies": <escaped JSON string in which I need to search for "com.example.commons/[email protected]" and similar strings>
}, ...]

Вот что я получил на данный момент:

jq '.[] | select(.classifier= = "APPLICATION") | {
      id: .name,
      dep1: ((.directDependencies // "") | capture(["com\\.example\\.commons/dep1@(?<version>[0-9.]+)"])),
      dep2: ((.directDependencies // "") | capture(["com\\.example\\.commons/dep2@(?<version>[0-9.]+)"])),
      dep3: ((.directDependencies // "") | capture(["com\\.example\\.commons/dep3@(?<version>[0-9.]+)"])),
      dep4: ((.directDependencies // "") | capture(["com\\.example\\.commons/dep4@(?<version>[0-9.]+)"]))
    }'

Сгенерированный вывод выглядит нормально:

{
  "id": "project1",
  "dep1": {
    "version": "0.102.0"
  },
  "dep2": {
    "version": "0.102.0"
  },
  "dep3": {
    "version": "0.102.0"
  },
  "dep4": {
    "version": "0.102.0"
  }
}
... // more elements

Проблема в том, что выходные данные содержат только те входные элементы, для которых все четыре capture чему-то соответствуют.

Я бы хотел, чтобы выходные данные содержали все элементы входного списка, даже если некоторые из capture не совпадают. Другими словами, я ожидаю, что на выходе появятся такие элементы, как:

{
    "id": "project1",
    "dep1": { "version": "0.1.0" },
    // dep2 didn't match
    // dep3 didn't match
    "dep4": { "version": "0.3.0" }
}

В документации такой опции не нашел. Есть идеи, как это решить?


1
50
2

Ответы:

Вы можете использовать подход, представленный на примере:

def capture($s; $default): capture($s) // $default;

.[]
| select(.classifier | test("APPLICATION"))
| {
      id: .name,
      dep1: ((.directDependencies // "") | capture(["com\\.example\\.commons/dep1@(?<version>[0-9.]+)"]; "dep1 did not match")),
      dep3: ((.directDependencies // "") | capture(["com\\.example\\.commons/dep3@(?<version>[0-9.]+)"]; "dep2 did not match")),
      dep4: ((.directDependencies // "") | capture(["com\\.example\\.commons/dep4@(?<version>[0-9.]+)"]; "dep1 did not match"))
  }


Решено

Насколько я понимаю вопрос, вы хотите добавлять в объект результата только успешные совпадения. Я бы предпочел использовать scan, чтобы найти все совпадения, а затем reduce поверх них, чтобы последовательно создавать объект результата, включая имя зависимости в регулярное выражение (например, как dep1|dep2|dep3|dep4, или короче dep[1-4], или в более общем смысле [^@]+, которое я использовал ниже).

.[] | select(.classifier/" | " | any(. == "APPLICATION")) | reduce (
  .directDependencies | scan("com\\.example\\.commons/(?<dep>[^@]+)@(?<version>[0-9.]+)")
) as [$dep, $version] ({id: .name}; .[$dep] = {$version})

Примечание № 1. Это всегда будет генерировать часть {id: .name}, даже если никакие зависимости не добавлены. Чтобы вместо этого проглотить эти «голые» объекты, начните сокращение с null и в конечном итоге отфильтруйте ненулевое значение values:

.[] | select(.classifier/" | " | any(. == "APPLICATION")) | {id: .name} + (reduce (
  .directDependencies | scan("com\\.example\\.commons/(?<dep>[^@]+)@(?<version>[0-9.]+)")
) as [$dep, $version] (null; .[$dep] = {$version}) | values)

Примечание № 2: Ваш фильтр select(.classifier= = "APPLICATION") соответствует только объектам, у которых значение под .classifier полностью равно строке "APPLICATION", и, таким образом, вообще не будет улавливать вводимый образец, поскольку он содержит {"classifier": "LIBRARY | APPLICATION"}. Поэтому вместо этого я использовал /, чтобы разделить это значение на подстроку " | ", и оно совпадало, если any (т. е. хотя бы одна) из полученных частей была равна этой строке.