CSV में मान और प्रारूप निकालने के लिए jq का उपयोग करना


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

मैं इस प्रारूप में एक सीएसवी फ़ाइल रखना चाहूंगा:

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

क्या यह केवल उपयोग करने से संभव है jq? मेरे पास कोई प्रोग्रामिंग कौशल नहीं है।


1
मैंने नीचे एक उत्तर प्रदान किया है, लेकिन मैं अब आपके प्रश्न के करीब देख रहा हूं और मैं यह सोचकर मदद नहीं कर सकता हूं - 6 वां VALUE कहां से आने वाला है?
24


इसके अलावा संबंधित stackoverflow.com/q/32960857/168034
phunehehe

जवाबों:


50

jq में एक फ़िल्टर है, @csv, एक सरणी को CSV स्ट्रिंग में बदलने के लिए। यह फ़िल्टर CSV प्रारूप से जुड़ी अधिकांश जटिलताओं को ध्यान में रखता है, जिसकी शुरुआत खेतों में लगे अल्पविराम से होती है। (टैब 1.5 में टैब-सेपरेटेड-वैल्यू फाइल जनरेट करने के लिए एक समान फ़िल्टर @tsv है।)

बेशक, यदि हेडर और मान सभी को अल्पविराम और दोहरे उद्धरण चिह्नों से मुक्त होने की गारंटी है, तो @csv फ़िल्टर का उपयोग करने की आवश्यकता नहीं हो सकती है। अन्यथा, इसका उपयोग करना बेहतर होगा।

उदाहरण के लिए, यदि 'कंपनी का नाम' 'स्मिथ, स्मिथ और स्मिथ' है, और यदि अन्य मान नीचे दिखाए गए हैं, तो "-r" विकल्प के साथ jq को लागू करने से वैध 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
मैं som jq somestuff | map (?) | @csv ', बहुत काम! धन्यवाद
flickerfly

3
आपका उदाहरण एक लाइन प्रति रिकॉर्ड होने के बजाय सभी प्रदर्शन नामों को पहली पंक्ति और दूसरी पंक्ति के सभी मानों पर रखने जा रहा है।
ब्रायन गॉर्डन

33

मैं अपने सीएसवी में प्रत्येक रिकॉर्ड को एक पंक्ति बनाना पसंद करता हूं।

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

2
क्या होगा अगर .value एक संख्या है? मुझे त्रुटि "स्ट्रिंग और संख्या को जोड़ा नहीं जा सकता है"
Cos

2
@ उपरोक्त उदाहरण के .value|tostringबजाय कुछ पसंद करें.value
matheeeny

4
@ मुझे लगता है कि कोष्ठक की आवश्यकता है। (.value|tostring)
ciscogambo

इसके अलावा, jq -rउद्धरणों का उपयोग करने के लिए उपयोग करें
क्ले

30

इस फ़ाइल को देखते हुए, आप कुछ ऐसा कर सकते हैं:

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

.ऑपरेटर एक वस्तु / हैश से एक क्षेत्र का चयन करता है। इस प्रकार, हम इसके साथ शुरू करते हैं .data, जो इसमें डेटा के साथ सरणी देता है। फिर हम दो बार सरणी पर मैप करते हैं, पहले डिस्प्लेनाम का चयन करते हैं, फिर मूल्य का चयन करते हुए, हमें उन कुंजियों के मानों के साथ दो सरणियाँ देते हैं। प्रत्येक सरणी के लिए, हम दो पंक्तियों को बनाते हुए "," तत्वों से जुड़ते हैं। -rतर्क कहता है jqजिसके परिणामस्वरूप तार बोली नहीं करने के लिए।

यदि आपकी वास्तविक फ़ाइल लंबी है (अर्थात, एक से अधिक लोगों के लिए प्रविष्टियाँ हैं), तो आपको कुछ अधिक जटिल होने की संभावना होगी।


यह मेरे लिए काम नहीं कर रहा है। संबंधित विषय में, उत्तर stackoverflow.com/questions/32960857/… दोनों काम कर रहा है और बहुत अच्छी तरह से समझाया गया है!
झुंड

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)लेकिन यह थोड़ा अधिक विशिष्ट है कि यह वांछित डेटा को पुनः प्राप्त करने के लिए गहराई के स्तर को निर्दिष्ट करता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.