web-dev-qa-db-ja.com

jqを使用して値を抽出し、CSVでフォーマットする

以下の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のみを使用して可能ですか?プログラミングのスキルはありません。

62
Kerim

jqには、配列をCSV文字列に変換するためのフィルター@csvがあります。このフィルターは、フィールドに埋め込まれたコンマで始まる、CSV形式に関連するほとんどの複雑さを考慮に入れます。 (jq 1.5には、タブ区切り値ファイルを生成するための同様のフィルター@tsvがあります。)

もちろん、ヘッダーと値のすべてにコンマと二重引用符がないことが保証されている場合は、@ csvフィルターを使用する必要がない場合があります。それ以外の場合は、おそらくそれを使用する方が良いでしょう。

たとえば、「会社名」が「Smith、Smith、Smith」であり、他の値が次のようになっている場合、「-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"
55
peak

各レコードをCSVの行にしたい。

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

このファイルだけで、次のようなことができます。

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

.演算子は、オブジェクト/ハッシュからフィールドを選択します。したがって、.data、データを含む配列を返します。次に、配列を2回マップします。最初にdisplayNameを選択し、次に値を選択して、これらのキーの値のみを持つ2つの配列を取得します。各配列について、要素を "、"で結合して2行を形成します。 -r引数は、結果の文字列を引用しないようにjqに指示します。

実際のファイルが長い場合(つまり、複数の人のエントリがある場合)、おそらくもう少し複雑なものが必要になります。

31
Steven D

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

Ruby JSONパーサーは、閉じ括弧の前の末尾のコンマについてバーフィードしました。

11
glenn jackman

この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
2
iruvar

jqが別の配列要素の期待について不平を言っていたので、それを機能させるために、サンプル入力の最後のコンマを削除する必要がありましたが、これは次のとおりです。

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

...私を得た...

_First Name, Last Name, Position, Company Name, Country
VALUE, VALUE, VALUE, VALUE, VALUE
_

一言で言えば、それがどのように機能するか:

  1. 空の_[]_インデックスフィールドフォームと_.dot_表記を使用して、データオブジェクトの第3レベルに移動しました。
  2. 十分に深くなったら、_.[][].displayName_のような名前で必要なデータフィールドを指定しました。
  3. _[.[][].displayName], [.[][].value]_のような個別の配列オブジェクトとして返すことで、希望するフィールドが自己関連付けされていることを確認しました
  4. 次に、それらのオブジェクトをjoin(", ")関数にパイプして、個別のエンティティとして結合しました。

実際には、_[.field]_を実行することはmap(.field)への別の方法にすぎませんが、目的のデータを取得するための深度レベルを指定するという点で、もう少し具体的です。

1
mikeserv