json / jq: многоуровневая группировка подэлементов в массиве - PullRequest
0 голосов
/ 24 февраля 2020

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

входящий json является массивом «задач». каждая задача [т.е. каждый элемент массива] - это объект, который выглядит следующим образом:

{
  "inputData": {
    "transfers": [
      {
        "source": {
          "directory": "/path/to/source",
          "filename": "somefile.mp3"
        },
        "target": {
          "directory": "/path/to/target",
          "filename": "somefile.mp3"
        }
      },
      {
        "source": {
          "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><delivery>content description</delivery>",
          "encoding": "UTF-8"
        },
        "target": {
          "directory": "/path/to/target",
          "filename": "somefile.xml"
        }
      }
    ]
  },
  "outputData": {
    "transferDuration": "00:00:37:10",
    "transferLength": 187813298,
  },
  "updateDate": "2020-02-21T14:37:18.329Z",
  "updateUser": "bob"
}

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

task[n].inputData.transfers[].target.filename, task[n].outputData.transferLength, task[n].updateDate

У меня есть цепочка фильтров, где он будет правильно выбирать соответствующие поля, даже там, где он выберет «правильную» единственную запись среди множества записей в массиве task[].inputData.transfers[] , но когда я пытаюсь получить вывод более чем одного элемента, цепочка повторяется по массиву три раза, и я получаю

task[0].inputData.transfers[].target.filename
task[1].inputData.transfers[].target.filename
task[2].inputData.transfers[].target.filename
... 
task[n].inputData.transfers[].target.filename

, а затем результаты поля outputData.transferLength для всех элементов,
тогда результаты поля updateDate для всех элементов.

вот моя цепочка фильтров:

'(.tasks[].inputData.transfers[] | select(.target.filename | match("[Xx][Mm][Ll]$")).target.filename), .tasks[].outputData.transferLength, .tasks[].updateDate'

я думаю, что должен быть какой-то эффективный способ сгруппировать все из этих многоуровневых элементов вместе для каждого элемента массива; что-то вроде ' with ... ', например with tasks[] : blablabla, но не могу понять, как это сделать. кто-нибудь может помочь?

Ответы [ 2 ]

0 голосов
/ 24 февраля 2020

я наконец нашел ответ. хитрость заключалась в том, чтобы передать .tasks[] в выражение, в котором парени были размещены вокруг элементов поля в виде группы, которая, очевидно, будет применять все, что находится внутри пареннов, к каждому элементу массива по отдельности, последовательно. затем, используя пример @dmitry в качестве руководства, я также поместил элементы в правую и левую скобки, чтобы воссоздать элементы массива, которые я затем мог бы выбрать, которые затем можно было бы вывести на 1 строку с | @csv. итоговая цепочка, которая сработала для меня:

.task[] | ([.inputData.transfers[].target.filename, .outputData.transferLength, .updateDate]) | [(.[0],.[2],.[3])] | @csv'

к сожалению, я не смог заставить match() работать в этом вызове или sub(); каждый из них заставлял jq выдавать бесполезное сообщение об ошибке непосредственно перед тем, как было выгружено ядро.

большое спасибо ответившим людям.

0 голосов
/ 24 февраля 2020

Пример JSON содержал лишнее ,, которое jq не может принять.

Кажется, что цепочка фильтров вашего примера работает на .tasks[], хотя пример выглядит как единственный задача. Поэтому невозможно переписать то, что вы получили, в рабочее состояние. Таким образом, вместо того, чтобы дать точный ответ на неточный вопрос, вот первая из трех частей вашей пересмотренной цепочки фильтров:

.inputData.transfers | map(select(.target.filename | match("xml$"; "i")))

См. этот фрагмент jqplay .

Вместо того, чтобы писать [ .xs[] | select(p) ], просто напишите .xs | map(select(p)).

...