JQ - денормализовать вложенный объект - PullRequest
1 голос
/ 20 января 2020

Я пытался преобразовать некоторые JSON в CSV, и у меня есть следующая проблема:

У меня есть следующий ввод json:

{"id": 100, "a": [{"t" : 1,"c" : 2 }, {"t": 2, "c" : 3 }] }
{"id": 200, "a": [{"t": 2, "c" : 3 }] }
{"id": 300, "a": [{"t": 1, "c" : 3 }] }

И я ожидаю следующий вывод CSV:

id,t1,t2
100,2,3
200,,3
300,3,

К сожалению, JQ не выводится, если один из селекторов не соответствует. Пример:

echo '{ "id": 100,  "a": [{"t" : 1,"c" : 2 }, {"t": 2, "c" : 3 }] }' | jq '{t1: (.a[] | select(.t==1)).c , t2: (.a[] | select(.t==2)).c }'

output:

{ "t1": 2,  "t2": 3   }

, но если один из выбранных объектов не возвращает соответствия, он не возвращается вообще. Пример:

echo '{ "id": 100,  "a": [{"t" : 1,"c" : 2 }] }' | jq '{t1: (.a[] | select(.t==1)).c , t2: (.a[] | select(.t==2)).c }' 

Ожидаемый результат:

{ "t1": 2,  "t2": null   }

Кто-нибудь знает, как этого добиться с помощью JQ?

РЕДАКТИРОВАТЬ:

На основе комментарий, сделанный @peak Я нашел решение, которое искал.

jq -r '["id","t1","t2"],[.id, (.a[] | select(.t==1)).c//null, (.a[] | select(.t==2)).c//null ]|@csv'

Альтернативный оператор делает именно то, что я искал. Альтернативный оператор

Ответы [ 2 ]

2 голосов
/ 20 января 2020

Вот простое решение, которое не предполагает ничего о порядке элементов в массиве .a и легко обобщает до произвольного числа значений .t:

# Convert an array of {t, c} to a dictionary:
def tod: map({(.t|tostring): .c}) | add;

["id", "t1", "t2"],   # header
(inputs 
 | (.a | tod) as $dict
 | [.id, (range(1;3) as $i | $dict[$i|tostring]) ])
| @csv

Параметры командной строки

Используйте параметр -n (потому что используется inputs) и параметр -r (для создания CSV).

0 голосов
/ 20 января 2020

Это абсолютный беспорядок, но он работает:

$ cat tmp.json
{"id": 100, "a": [{"t" : 1,"c" : 2 }, {"t": 2, "c" : 3 }] }
{"id": 200, "a": [{"t": 2, "c" : 3 }] }
{"id": 300, "a": [{"t": 1, "c" : 3 }] }
$ cat filter.jq
def t(id):
  .a |
  map({key: "t\(.t)", value: .c}) |
  ({t1:null, t2:null, id:id} | to_entries) + . | from_entries
  ;

inputs |
  map(.id as $id | t($id)) |
  (.[0] | keys) as $hdr |
  ([$hdr] + map(to_entries |map(.value)))[]|
  @csv
$ jq -rn --slurp -f filter.jq tmp.json
"id","t1","t2"
2,3,100
,3,200
3,,300

Короче говоря, вы создаете прямой объект, содержащий значения из вашего ввода, а затем добавляете его в объект "по умолчанию", чтобы заполнить недостающие ключи.

...