Menggunakan jq untuk mengekstrak nilai dan format dalam CSV

58

Saya memiliki file JSON di bawah ini:

{
"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"
    },
]
}

Saya ingin memiliki file CSV dalam format ini:

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

Apakah ini mungkin hanya dengan menggunakan jq? Saya tidak memiliki keterampilan pemrograman.

Kerim
sumber
1
Saya memberikan jawaban di bawah ini, tetapi saya sekarang melihat lebih dekat pada pertanyaan Anda dan saya tidak dapat berhenti bertanya-tanya - dari mana VALUE ke-6 seharusnya berasal?
mikeserv
1
Terkait dari SO: stackoverflow.com/questions/25558456/…
Anton Tarasenko
Juga terkait stackoverflow.com/q/32960857/168034
phunehehe

Jawaban:

50

jq memiliki filter, @csv, untuk mengonversi array ke string CSV. Filter ini memperhitungkan sebagian besar kompleksitas yang terkait dengan format CSV, dimulai dengan koma yang tertanam di bidang. (jq 1.5 memiliki filter serupa, @tsv, untuk menghasilkan file nilai yang dipisahkan-tab.)

Tentu saja, jika tajuk dan nilai semuanya dijamin bebas dari koma dan tanda kutip ganda, maka mungkin tidak perlu menggunakan filter @csv. Kalau tidak, mungkin akan lebih baik menggunakannya.

Misalnya, jika 'Nama Perusahaan' adalah 'Smith, Smith dan Smith', dan jika nilai-nilai lain seperti yang ditunjukkan di bawah ini, memohon jq dengan opsi "-r" akan menghasilkan CSV yang valid:

$ 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"
puncak
sumber
3
Saya bisa 'jq somestuff | peta (.) | @csv ', sangat berguna! Terima kasih
flickerfly
3
Contoh Anda akan meletakkan semua nama tampilan di baris pertama dan semua nilai di baris kedua, alih-alih memiliki satu baris per catatan.
Brian Gordon
33

Saya lebih suka membuat setiap rekaman satu baris di CSV saya.

jq '.data | map([.displayName, .rank, .value] | join(", ")) | join("\n")'
Silas Paul
sumber
2
Bagaimana jika .value adalah angka? Saya menerima kesalahan "string dan angka tidak dapat ditambahkan"
Cos
2
@ Pilih sesuatu seperti .value|tostringbukannya .valuepada contoh di atas
matheeeny
4
@Cos, saya menemukan tanda kurung diperlukan. (.value|tostring)
ciscogambo
Juga, gunakan jq -runtuk menghapus tanda kutip
Clay
30

Diberi hanya file ini, Anda dapat melakukan sesuatu seperti:

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

The .Operator memilih field dari obyek / hash. Jadi, kita mulai dengan .data, yang mengembalikan array dengan data di dalamnya. Kami kemudian memetakan array dua kali, pertama memilih displayName, lalu memilih nilai, memberi kami dua array hanya dengan nilai-nilai kunci tersebut. Untuk setiap array, kita gabungkan elemen dengan "," membentuk dua garis. The -rArgumen mengatakan jquntuk tidak mengutip string yang dihasilkan.

Jika file Anda yang sebenarnya lebih panjang (yaitu, memiliki entri untuk lebih dari satu orang), Anda mungkin perlu sesuatu yang sedikit lebih rumit.

Steven D
sumber
Itu tidak bekerja untuk saya. Dalam topik terkait, jawabannya stackoverflow.com/questions/32960857/… berfungsi dan dijelaskan dengan sangat baik!
herve
10

Saya merasa jqsulit untuk membungkus kepala saya. Ini beberapa 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

Parser JSON ruby ​​muntah tentang koma trailing sebelum tanda kurung tutup.

glenn jackman
sumber
2

Karena Anda menandai ini pythondan dengan asumsi nama jsonfilex.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
iruvar
sumber
1

Meskipun saya harus menghapus koma terakhir dalam contoh input Anda untuk membuatnya berfungsi karena jqmengeluh tentang mengharapkan elemen array lain, ini:

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

... menangkapku ...

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

Singkatnya cara kerjanya:

  1. Saya melintasi ke tingkat ketiga objek data menggunakan []formulir bidang kosong dan .dotnotasi indeks .
  2. Setelah cukup dalam saya tentukan bidang data yang saya inginkan dengan nama seperti .[][].displayName.
  3. Saya meyakinkan bahwa bidang yang saya inginkan terkait sendiri dengan mengembalikannya sebagai objek array terpisah seperti [.[][].displayName], [.[][].value]
  4. Dan kemudian menyalurkan objek-objek tersebut ke join(", ")fungsi yang akan digabungkan sebagai entitas yang terpisah.

Sebenarnya melakukan [.field]hanyalah cara lain untuk map(.field)tetapi ini sedikit lebih spesifik karena ia menentukan tingkat kedalaman untuk mengambil data yang diinginkan.

mikeserv
sumber