Тощий
jq -r '(.[0] | keys_unsorted) as $keys | $keys, map([.[ $keys[] ]])[] | @csv'
или:
jq -r '(.[0] | keys_unsorted) as $keys | ([$keys] + map([.[ $keys[] ]])) [] | @csv'
Детали
В стороне
Описать детали сложно, потому что jq ориентирован на поток, то есть он работает с последовательностью данных JSON, а не с одним значением. Входной поток JSON преобразуется в некоторый внутренний тип, который проходит через фильтры, а затем кодируется в потоке вывода в конце программы. Внутренний тип не моделируется JSON и не существует как именованный тип. Это проще всего продемонстрировать, изучив вывод простого индекса ( .[]
) или оператора запятой (непосредственное изучение этого может быть выполнено с помощью отладчика, но это будет с точки зрения внутренних типов данных jq, а не концептуальных типов данных, стоящих за JSON) .
$ jq -c '. []' <<< '["a", "b"]'
"а"
"б"
$ jq -cn '"а", "б"'
"а"
"б"
Обратите внимание, что вывод не является массивом (что могло бы быть ["a", "b"]
). Компактный вывод ( -c
опция) показывает, что каждый элемент массива (или аргумент ,
фильтра) становится отдельным объектом в выводе (каждый находится в отдельной строке).
Поток похож на JSON-seq , но использует символы новой строки, а не RS в качестве разделителя вывода при кодировании. Следовательно, этот внутренний тип упоминается в этом ответе общим термином «последовательность», причем «поток» зарезервирован для закодированных входных и выходных данных.
Построение фильтра
Ключи первого объекта можно извлечь с помощью:
.[0] | keys_unsorted
Ключи обычно хранятся в исходном порядке, но сохранение точного порядка не гарантируется. Следовательно, их нужно будет использовать для индексации объектов, чтобы получить значения в том же порядке. Это также предотвратит попадание значений в неправильные столбцы, если некоторые объекты имеют другой порядок ключей.
Чтобы вывести ключи в качестве первой строки и сделать их доступными для индексации, они сохраняются в переменной. Затем следующий этап конвейера ссылается на эту переменную и использует оператор запятой для добавления заголовка к выходному потоку.
(.[0] | keys_unsorted) as $keys | $keys, ...
Выражение после запятой немного запутано. Оператор индекса объекта может принимать последовательность строк (например "name", "value"
), возвращая последовательность значений свойств для этих строк. $keys
представляет собой массив, а не последовательность, поэтому []
применяется для преобразования его в последовательность,
$keys[]
который затем может быть передан .[]
.[ $keys[] ]
Это тоже создает последовательность, поэтому конструктор массива используется для преобразования ее в массив.
[.[ $keys[] ]]
Это выражение должно применяться к одному объекту. map()
используется для применения ко всем объектам внешнего массива:
map([.[ $keys[] ]])
Наконец, на этом этапе он преобразуется в последовательность, поэтому каждый элемент становится отдельной строкой на выходе.
map([.[ $keys[] ]])[]
Зачем объединять последовательность в массив внутри map
только для того, чтобы разделить ее снаружи? map
производит массив; .[ $keys[] ]
производит последовательность. Применение map
к последовательности from .[ $keys[] ]
приведет к созданию массива последовательностей значений, но поскольку последовательности не относятся к типу JSON, вместо этого вы получите плоский массив, содержащий все значения.
["NSW","AU","state","New South Wales","AB","CA","province","Alberta","ABD","GB","council area","Aberdeenshire","AK","US","state","Alaska"]
Значения каждого объекта должны храниться отдельно, чтобы они становились отдельными строками в окончательном выводе.
Наконец, последовательность проходит через @csv
форматтер.
Альтернативный
Предметы можно разделить поздно, а не раньше. Вместо использования оператора запятой для получения последовательности (передача последовательности в качестве правого операнда) заголовок sequence ( $keys
) может быть заключен в массив и +
использоваться для добавления массива значений. Это все еще необходимо преобразовать в последовательность перед передачей в @csv
.
json2csv