Как получить ключ и значение из файла JSON, используя параметр потока в jq - PullRequest
0 голосов
/ 12 ноября 2018

У меня есть файл json со структурой, подобной этой:

 {
    "A": [{
        "B": {
            "C": [{
                "D": {
                    "applicationNumberText": {
                        "value": "15570075",
                        "electronicText": "15570075"
                    },
                    "date": "2018-10-01",
                    "app": "Utility"
                }
            }]
        }
    }]
}

Теперь я хочу получить значение electronicText.Одним из способов является использование индексов, таких как:

jq --stream 'select(.[0][1] == "A" and .[0][2] == "B" and .[0][3] == "C") | .[1]'

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

cat file.json | jq --stream 'select(.A|.[]. B. C|.[]. D.applicationNumberText)'

Поэтому я хочу получить ключ electronicText без использования индексов.

Note: я хочу использовать опцию stream для большого файла JSON.

Ответы [ 4 ]

0 голосов
/ 13 ноября 2018

Если вы знаете название объектов, представляющих интерес, просто ищите пути к объектам по имени.

getpath(paths(objects) | select(.[-1] == "applicationNumberText"))

Потоковый ввод должен помочь с соображениями эффективности.Просто укажите пути, которые вы хотите сохранить, и обрежьте пути, представляющие интерес.

$ jq -n --stream --arg key 'applicationNumberText' '
fromstream(inputs | truncate_stream2((.[0] | index($key) // empty) + 1))
' input.json

Это просто проверяет путь и ищет ключ "applicationNumberText", затем усекает путь к этому значению, чтобы его можно было восстановить изпоток.

При этом используется инвертированная версия функции truncate_stream/1, которая меняет обычные входные данные, которые я нахожу, используя более интуитивно понятный.

def truncate_stream2($count): .[0] |= .[$count:];
0 голосов
/ 12 ноября 2018

Я верю, что вы ищете это:

. as $inputs | {} | setpath($inputs[0]; $inputs[1]) | .A[]?.B.C[]?.D.applicationNumberText.electronicText // empty

Этот фильтр ожидает --stream в качестве флага (как ваш первый пример), генерирует временной объект с разделом json, запускает фильтр на основе вашего второго примера, а затем скрывает нулевые результаты. Примечание: Это основано на примере из поваренной книги

Позвольте мне дать вам общее представление об играх на случай, если это сбивает с толку:

  1. Флаг --stream заставит вас получать ваши данные иначе . Теперь ваш фильтр будет запускаться несколько раз с данными в виде [<path>, <leaf-value>], полученными при прохождении парсером вашего файла json, по одному узлу за раз.

    Я полагаю, вы ссылаетесь на это, когда упоминаете об использовании индексов, в данном случае это означает, что вы проверяете path expression value, который --streaming flag предоставляет вместо фактических данных json.

    Пожалуйста, сравните выходные данные следующего:

    jq '.A[].B.C[].D.applicationNumberText.electronicText' file.json # outputs 15570075 
    jq --stream '.A[].B.C[].D.applicationNumberText.electronicText' file.json # multiple failures: cannot index array with string "A"
    
  2. setpath () получает эти пути, а параметры листового значения помещают его поверх входного объекта. например, * * одна тысяча тридцать две

    {} | setpath(["a", 0, "b"], "leaf-value") # returns {"a":[{"b":"leaf-value"}]}
    

    в нашем случае мы генерируем следующий поток значений при разборе каждого узла:

    {"A":[{"B":{"C":[{"D":{"applicationNumberText":{"value":"15570075"}}}]}}]}
    {"A":[{"B":{"C":[{"D":{"applicationNumberText":{"electronicText":"15570075"}}}]}}]}
    {"A":[{"B":{"C":[{"D":{"applicationNumberText":{"electronicText":null}}}]}}]}
    {"A":[{"B":{"C":[{"D":{"date":"2018-10-01"}}]}}]}
    {"A":[{"B":{"C":[{"D":{"app":"Utility"}}]}}]}
    
  3. Теперь, когда данные имеют формат, подобный файлу json, мы можем запустить более знакомый фильтр.

    .A[]?.B.C[]?.D.applicationNumberText.electronicText
    

    имейте в виду, что между ними нет пробелов. Мы также используем оператор ? для обоих генераторов .A[] и .C[], потому что мы не уверены, что эти поля будут доступны как массивы постоянно

В качестве заключительного замечания вы можете рассмотреть возможность создания массива пути с path() и сравнить его со значением пути, которое появляется в .[0] при использовании --stream jq flag

0 голосов
/ 12 ноября 2018

В ответ на оригинальный вопрос:

jq --stream '
  select(length==2 and .[0][-1]=="electronicText")|.[1]
' input.json
"15570075"

Если вы также хотите value, то вы можете рассмотреть следующий фильтр:

select(length==2 and .[0][-2]=="applicationNumberText")
| .[0][-1] as $last
| select($last == "electronicText" or $last == "value")
| {($last): .[1]}

, который с вашим образцом JSON производит:

{"value":"15570075"}
{"electronicText":"15570075"}

Объединение пар ключ-значение в один объект JSON

Одним из способов создания «словаря» является использование inputs в сочетании с параметром командной строки -n. Просто оберните вышеупомянутый фильтр в структуру:

 [inputs | ....] | add

где здесь .... обозначает вышеуказанный фильтр; и вызовите jq с и опциями -n и --stream.

0 голосов
/ 12 ноября 2018

Если индексы массивов фиксированы, вы можете использовать этот фильтр:

jq '.A[0].B.C[0].D.applicationNumberText.electronicText' file

и для извлечения всех полей electronicText, вы можете использовать это:

jq '.A[].B.C[].D.applicationNumberText.electronicText' file

Если вы хотите использовать потоковую моду jq (хотя неясно, почему), вы можете использовать это:

jq --stream 'select(.[0]|contains(["electronicText"]))|.[1]//empty'

Это будет искать в ключевом пути строку electronicText и, если она найдена, получит соответствующее значение. //empty - отфильтровать массив пути без значения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...