Худий
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"]'
"a"
"б"
$ jq -cn '"a", "b"'
"a"
"б"
Зауважте, що вихід не є масивом (який би був ["a", "b"]
). Компактний вихід ( -c
опція) показує, що кожен елемент масиву (або аргумент до ,
фільтра) стає окремим об'єктом у висновку (кожен знаходиться в окремому рядку).
Потік схожий на JSON-послідовність , але він використовує нові рядки, а не 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
до послідовності з .[ $keys[] ]
буде створювати масив послідовностей значень, але оскільки послідовності не є типом JSON, то натомість ви отримуєте сплющений масив, що містить усі значення.
["NSW","AU","state","New South Wales","AB","CA","province","Alberta","ABD","GB","council area","Aberdeenshire","AK","US","state","Alaska"]
Значення кожного об'єкта потрібно зберігати окремо, щоб вони стали окремими рядками у кінцевому висновку.
Нарешті послідовність передається через @csv
форматер.
Чергуйте
Елементи можна розділяти пізно, а не рано. Замість використання оператора комами для отримання послідовності (передаючи послідовність як правильний операнд) послідовність заголовка ( $keys
) може бути загорнута у масив та +
використана для додавання масиву значень. Це все ще потрібно перетворити на послідовність, перш ніж передати його @csv
.
json2csv