Використання jq для вилучення значень та формату в CSV


58

У мене є файл JSON нижче:

{
"data": [
    {
        "displayName": "First Name",
        "rank": 1,
        "value": "VALUE"
    },
    {
        "displayName": "Last Name",
        "rank": 2,
        "value": "VALUE"
    },
    {
        "displayName": "Position",
        "rank": 3,
        "value": "VALUE"
    },
    {
        "displayName": "Company Name",
        "rank": 4,
        "value": "VALUE"
    },
    {
        "displayName": "Country",
        "rank": 5,
        "value": "VALUE"
    },
]
}

Я хотів би мати файл CSV у такому форматі:

First Name, Last Name, Position, Company Name, Country
VALUE, VALUE, VALUE, VALUE, VALUE, VALUE

Це можливо лише за допомогою jq? У мене немає ніяких навичок програмування.


1
Я дала відповідь нижче, але я тепер придивившись на ваше запитання , і я не перестаю дивуватися - де це шостий VALUE повинен виходити від?
mikeserv


Також в зв'язку stackoverflow.com/q/32960857/168034
phunehehe

Відповіді:


50

jq має фільтр @csv для перетворення масиву в рядок CSV. Цей фільтр враховує більшість складностей, пов’язаних із форматом CSV, починаючи з коми, вбудованих у поля. (jq 1.5 має подібний фільтр, @tsv, для створення файлів, що розділені за значеннями.)

Звичайно, якщо всі заголовки та значення гарантовано не містять коми та подвійних лапок, можливо, не потрібно використовувати фільтр @csv. Інакше було б, мабуть, краще використовувати його.

Наприклад, якщо "Назва компанії" - "Сміт, Сміт і Сміт", а якщо інші значення були такими, як показано нижче, виклик jq за допомогою параметра "-r" призведе до дійсного CSV:

$ jq -r '.data | map(.displayName), map(.value) | @csv' so.json2csv.json
"First Name","Last Name","Position","Company Name","Country"
"John (""Johnnie"")","Doe","Director, Planning and Posterity","Smith, Smith and Smith","Transylvania"

3
Мені вдалося 'jq somestuff | карта (.) | @csv ', дуже зручно! Спасибі
мерехтіння

3
Ваш приклад буде ставити всі відображувані імена в перший рядок, а всі значення у другий рядок, а не один рядок на запис.
Брайан Гордон

33

Я вважаю за краще робити кожен запис рядком у своєму CSV.

jq '.data | map([.displayName, .rank, .value] | join(", ")) | join("\n")'

2
Що робити, якщо .value - це число? Я отримую помилку "рядок і номер не можна додати"
Cos

2
@Cos щось подібне .value|tostringзамість .valueвищенаведеного прикладу
matheeeny

4
@Cos, я виявив, що дужки потрібні. (.value|tostring)
ciscogambo

Також використовуйте jq -rдля зняття цитат
Глина

30

Враховуючи саме цей файл, ви можете зробити щось на кшталт:

<testfile jq -r '.data | map(.displayName), map(.value) | join(", ")'

.Оператор вибирає поле з об'єкта / хеша. Таким чином, ми почнемо з того .data, що повертає масив з даними, що знаходяться в ньому. Потім ми двічі відображаємо масив, спочатку вибираючи displayName, потім вибираємо значення, надаючи нам два масиви із лише значеннями цих клавіш. Для кожного масиву ми з'єднуємо елементи ",", утворюючи два рядки. -rАргумент говорить jqне процитувати отримані рядки.

Якщо ваш фактичний файл довший (тобто він містить записи для однієї людини), вам, швидше за все, знадобиться щось складніше.


Це не працює для мене. У відповідній темі відповідь stackoverflow.com/questions/32960857/… працює і дуже добре пояснена!
herve

10

Мені jqважко було обернути голову. Ось деякі Ruby:

ruby -rjson -rcsv -e '
  data = JSON.parse(File.read "file.json")
  data["data"].collect {|item| [item["displayName"], item["value"]]}
              .transpose
              .each {|row| puts row.to_csv}
'
First Name,Last Name,Position,Company Name,Country
VALUE,VALUE,VALUE,VALUE,VALUE

Рубіновий аналізатор JSON заговорив перед комою перед тісною дужкою.


2

Так як ви позначили це pythonі припускаючи, що ім'я jsonфайлу єx.json

import os, json
with open('x.json') as f:
    x  = json.load(f)
    print '{}{}{}'.format(', '.join(y['displayName'] for y in x['data']), os.linesep,
             ', '.join(y['value'] for y in x['data']))
First Name, Last Name, Position, Company Name, Country
VALUE, VALUE, VALUE, VALUE, VALUE

1

Хоча мені довелося видалити останню кому з вашого прикладу введення, щоб вона працювала, оскільки jqскаржилася на очікування іншого елемента масиву, це:

INPUT | jq -r '[.[][].displayName], [.[][].value]| join(", ")'

... отримав мене ...

First Name, Last Name, Position, Company Name, Country
VALUE, VALUE, VALUE, VALUE, VALUE

Як це працює в двох словах:

  1. Я перейшов до третього рівня об'єктів даних, використовуючи порожню []форму індексу поля та .dotпозначення.
  2. Досить глибоко я вказав поля даних, які я хотів по імені .[][].displayName.
  3. Я запевнив, що потрібні поля були пов’язані самостійно, повертаючи їх як окремі об’єкти масиву [.[][].displayName], [.[][].value]
  4. А потім передайте ці об'єкти у join(", ")функцію, яку слід об'єднати як окремі об'єкти.

По правді кажучи, [.field]це просто інший спосіб, map(.field)але це трохи більш специфічно, оскільки він визначає рівень глибини для отримання потрібних даних.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.