Извлечение данных из объектов, расположенных на разной глубине, в огромном файле JSON - PullRequest
0 голосов
/ 23 января 2019

Я использовал следующий код для извлечения данных из огромного файла формата json.

_ITEM_L1=`cat json.list | jq .item[].length | wc -l`
for (( i=0;i<$_ITEM_L1;i++ ));
do
    _ITEM_L2=`cat json.list | jq .item[$i].item[].length | wc -l`
    for (( j=0;j<$_ITEM_L2;j++ ));
    do
        _API_NAME=`cat json.list | jq .item[$i].item[$j].name`
        _API_URL=`cat json.list | jq .item[$i].item[$j].request.url.raw`
        echo $_API_NAME
        echo $_API_URL;
    done;
done

Сначала я думаю, что это всего лишь 2 уровня, но когда я запускаю скрипт, я обнаружил, что его более 2 уровня, онможет быть 3 или 4 или 5 или более.Поэтому мой вопрос заключается в том, как узнать, сколько уровней в файле json, прежде чем начать итерацию?

item0
    |
    item0
         |
         item0
              name:
              url:
         item1
              name:
              url:
         item2
              name:
              url:
    item1
         |
         item0
              |
              item0
                   name:
                   url:
              item1
                   name:
                   url:
         item1
              |
              item0
                   name:
                   url:
              item1
                   name:
                   url:
              item2
                   name:
                   url:
item1
     |
     item0
          name:
          url:
     item1
          name:
          url:
     item2
          name:
          url:
.
.
.
.
.
.

1 Ответ

0 голосов
/ 12 февраля 2019

Лучший дизайн не потребует , чтобы вы знали, сколько уровней глубины вложения в вашем файле, и определенно не будет вызывать jq снова и сновав петле!(jq - это полноценный язык программирования - он может выполнять цикл самостоятельно, и гораздо эффективнее обрабатывать весь файл всего за один вызов jq, а не вызывать его снова и снова с разными фильтрами).


Давайте начнем с конкретного примера ввода:

{
  "item0": {
    "item0a": {
      "item0aA": {
        "name": "foo",
        "url": "bar"
      },
      "item0aB": {
        "name": "baz",
        "url": "qux"
      }
    }
  },
  "item1": {
    "name": "qux",
    "url": "quux"
  }
}

Чтобы преобразовать это в сплющенный набор имен / URL-адресов, можно использовать:

jq -r '.. | objects | select(.name? != null) | [ .name, .url ] | @tsv'

который выдаст в качестве вывода:

foo bar
baz qux
qux quux

... который вы можете тривиально перебрать в bash:

while IFS=$'\t' read -r name url; do
  echo "Read name $name and url $url"
done < <(jq -r '.. | objects | select(.name? != null) | [ .name, .url ] | @tsv' <json.list)

Разбить, как это работает:

  • .. - это оператор рекурсивного спуска jq.
  • objects игнорирует вещи, которые не являются объектами.
  • .name? != null фильтрует только те объекты, которые имеют имена.(Подобным же образом можно фильтровать только те объекты, которые имеют URL-адреса одинаково).
  • @tsv помещает выходные данные в разделенную табуляцией форму значения.
  • IFS=$'\t' read -r name url считывает строку ввода впеременные name и url с вкладкой, разделяющей их.
...