การใช้ jq เพื่อแยกค่าและรูปแบบใน CSV


57

ฉันมีไฟล์ 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 ที่ 6 ควรมาจากไหน
mikeserv

1
เกี่ยวข้องจาก SO: stackoverflow.com/questions/25558456/…
Anton Tarasenko

คำตอบ:


49

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 'มีประโยชน์มาก! ขอบคุณ
flickerfly

3
ตัวอย่างของคุณจะใส่ชื่อที่แสดงทั้งหมดในบรรทัดแรกและค่าทั้งหมดในบรรทัดที่สองแทนที่จะมีหนึ่งบรรทัดต่อระเบียน
Brian Gordon

32

ฉันต้องการให้แต่ละแถวบันทึกใน CSV ของฉัน

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

2
เกิดอะไรขึ้นถ้า. มูลค่าเป็นจำนวนหรือไม่ ฉันได้รับข้อผิดพลาด "ไม่สามารถเพิ่มสตริงและหมายเลข"
Cos

2
@Cos บางสิ่งบางอย่างเช่น.value|tostringแทนที่จะเป็น.valueในตัวอย่างข้างต้น
matheeeny

4
@Cos ฉันพบว่าต้องใช้วงเล็บ (.value|tostring)
ciscogambo

และใช้jq -rเพื่อตัดเครื่องหมายคำพูด
Clay

30

ด้วยไฟล์นี้คุณสามารถทำสิ่งต่อไปนี้:

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

.ผู้ประกอบการเลือกฟิลด์จากวัตถุ / กัญชา ดังนั้นเราเริ่มต้นด้วย.dataซึ่งส่งกลับอาร์เรย์ด้วยข้อมูลในนั้น จากนั้นเราแมปไปที่อาร์เรย์สองครั้งก่อนเลือก displayName จากนั้นเลือกค่าทำให้เรามีสองอาร์เรย์ที่มีเพียงค่าของคีย์เหล่านั้น สำหรับแต่ละอาร์เรย์เราเข้าร่วมองค์ประกอบที่มี "," สร้างสองบรรทัด -rอาร์กิวเมนต์บอกjqไม่พูดสตริงที่เกิด

หากไฟล์จริงของคุณยาวกว่านั้น (เช่นมีรายการมากกว่าหนึ่งคน) คุณอาจต้องการบางสิ่งที่ซับซ้อนกว่านี้เล็กน้อย


มันไม่ทำงานสำหรับฉัน ในหัวข้อที่เกี่ยวข้องคำตอบstackoverflow.com/questions/32960857/…นั้นทำงานได้ดีและอธิบายได้ดีมาก!
Herve

10

ฉันพบjqว่ายากที่จะห่อหัวของฉันไปรอบ ๆ นี่คือทับทิมบางส่วน:

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)แต่นี่เป็นเพียงเล็กน้อยที่เฉพาะเจาะจงมากขึ้นในการที่จะระบุระดับความลึกสำหรับการดึงข้อมูลที่ต้องการ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.