Как использовать jq, чтобы найти все пути к определенному ключу

В очень большой вложенной структуре json я пытаюсь найти все пути, заканчивающиеся на ключе.

например:

{
  "A": {
    "A1": {
      "foo": {
        "_": "_"
      }
    },
    "A2": {
      "_": "_"
    }
  },
  "B": {
    "B1": {}
  },
  "foo": {
    "_": "_"
  }
}

будет печатать что-то вроде: ["A","A1","foo"], ["foo"]


К сожалению, я не знаю, на каком уровне вложенности будут появляться ключи, поэтому я не смог разобраться с помощью простого выбора. Я сблизился с jq '[paths] | .[] | select(contains(["foo"]))', но вывод содержит все перестановки любого дерева, содержащего foo. выход: ["A", "A1", "foo"]["A", "A1", "foo", "_"]["foo"][ "foo", "_"]

Бонусные баллы, если бы я мог сохранить исходный формат структуры данных, но просто отфильтровать все пути, которые не содержат ключ (в этом случае вложенные деревья в "foo" не нужно было бы скрывать).

2 ответа

Решение

С вашим вкладом:

$ jq -c 'paths | select(.[-1] == "foo")' 
["A","A1","foo"]
["foo"]

Бонусные очки:

(1) Если ваш JQ имеет tostream:

$ jq 'fromstream(tostream| select(.[0]|index("foo")))'

Или еще лучше, так как ваш ввод велик, вы можете использовать потоковый парсер (jq -n --stream) с этим фильтром:

fromstream( inputs|select( (.[0]|index("foo"))))

(2) Есть ли у вашего JQ tostream:

. as $in
| reduce (paths(scalars) | select(index("foo"))) as $p
    (null; setpath($p; $in|getpath($p))))

Во всех трех случаях вывод:

{
  "A": {
    "A1": {
      "foo": {
        "_": "_"
      }
    }
  },
  "foo": {
    "_": "_"
  }
}

У меня была такая же фундаментальная проблема.

С вводом (yaml), например:

      developer:
  android:
    members:
    - alice
    - bob
    oncall:
    - bob
hr:
  members:
  - charlie
  - doug
this:
  is:
    really:
      deep:
        nesting:
          members:
          - example deep nesting

Я хотел найти все произвольно вложенные группы и получить их участников.

Используя это:

      yq . | # convert yaml to json using python-yq
    jq ' 
    . as $input | # Save the input for later
    . | paths | # Get the list of paths 
        select(.[-1] | tostring | test("^(members|oncall|priv)$"; "ix")) | # Only find paths which end with members, oncall, and priv
        . as $path | # save each path in the $path variable
    ( $input | getpath($path) ) as $members | # Get the value of each path from the original input
    {
        "key": ( $path | join("-") ), # The key is the join of all path keys
        "value": $members  # The value is the list of members
    }
    ' |
    jq -s 'from_entries' | # collect kv pairs into a full object using slurp
    yq --sort-keys -y . # Convert back to yaml using python-yq

Я получаю такой вывод:

      developer-android-members:
  - alice
  - bob
developer-android-oncall:
  - bob
hr-members:
  - charlie
  - doug
this-is-really-deep-nesting-members:
  - example deep nesting
Другие вопросы по тегам