web-dev-qa-db-ja.com

jqを使用したCSVからJSONへの変換

次のようなcsvデータセットがある場合:

name, age, gender
john, 20, male
jane, 30, female
bob, 25, male

これに到達できますか:

[ {"name": "john", "age": 20, "gender: "male"},
  {"name": "jane", "age": 30, "gender: "female"},
  {"name": "bob", "age": 25, "gender: "male"} ]

jqのみを使用していますか?

私は this 記事を見つけましたが、これは私がやろうとしていることを示していますが、ヘッダーフィールドと値の「手動」マッピングを使用しています。ヘッダーフィールドの名前を変更する必要はありません。また、レイアウトが変わるたびにスクリプトやコマンドを変更する必要もありません。

ヘッダーを動的に抽出し、jqワンライナーで値と結合することは可能ですか?

20
jpl1079

要するに-はい、多分ワンライナービットを除いて。

jqはテキストのラングリングに適していることが多く、これは特に正規表現をサポートするバージョンに当てはまります。たとえば、正規表現のサポートを使用すると、特定の問題ステートメントで必要なトリミングは簡単です。

Jq 1.5rc1は正規表現サポートを含み、2015年1月1日から利用可能であるため、次のプログラムはjq 1.5のバージョンを想定しています。 jq 1.4で動作させる場合は、2つの「For jq 1.4」コメントを参照してください。

また、このプログラムはCSVの一般性と複雑さのすべてを処理するわけではないことにも注意してください。 (CSVをより一般的に処理する同様のアプローチについては、 https://github.com/stedolan/jq/wiki/Cookbook#convert-a-csv-file-with-headers-to-json を参照してください=)

# objectify/1 takes an array of string values as inputs, converts
# numeric values to numbers, and packages the results into an object
# with keys specified by the "headers" array
def objectify(headers):
  # For jq 1.4, replace the following line by: def tonumberq: .;
  def tonumberq: tonumber? // .;
  . as $in
  | reduce range(0; headers|length) as $i ({}; .[headers[$i]] = ($in[$i] | tonumberq) );

def csv2table:
  # For jq 1.4, replace the following line by:  def trim: .;
  def trim: sub("^ +";"") |  sub(" +$";"");
  split("\n") | map( split(",") | map(trim) );

def csv2json:
  csv2table
  | .[0] as $headers
  | reduce (.[1:][] | select(length > 0) ) as $row
      ( []; . + [ $row|objectify($headers) ]);

csv2json

例(csv.csvが指定のCSVテキストファイルであると想定):

$ jq -R -s -f csv2json.jq csv.csv
[
  {
    "name": "john",
    "age": 20,
    "gender": "male"
  },
  {
    "name": "jane",
    "age": 30,
    "gender": "female"
  },
  {
    "name": "bob",
    "age": 25,
    "gender": "male"
  }
]
24
peak

miller( http://johnkerl.org/miller/doc/ )を使用すると非常に簡単です。このinput.csvファイルを使用する

name,age,gender
john,20,male
jane,30,female
bob,25,male

と実行

mlr --c2j --jlistwrap cat input.csv

あなたは

[
{ "name": "john", "age": 20, "gender": "male" }
,{ "name": "jane", "age": 30, "gender": "female" }
,{ "name": "bob", "age": 25, "gender": "male" }
]
8
aborruso

私は少し遊びをして、これを思いつきました。しかし、それは最善の方法ではないかもしれません、あなたの試みがどのようなものであったかを見てみたいと思います両方が解決策にたどり着いたら、それは2倍良いと確信しています!

しかし、私は次のようなものから始めます:

_true as $doHeaders
| . / "\n"
| map(. / ", ")
| (if $doHeaders then .[0] else [range(0; (.[0] | length)) | tostring] end) as $headers
| .[if $doHeaders then 1 else 0 end:][]
| . as $values
| keys
| map({($headers[.]): $values[.]})
_

使用例

変数_$doHeaders_は、先頭行をヘッダー行として読み取るかどうかを制御します。あなたの場合、あなたはそれを本当のようにしたいのですが、私は将来のためにそれを追加しましたSO usersそして、まあ、今日私は素晴らしい朝食を食べました、そして天気が良いので、なぜですか?

少し説明:

1)_. / "\n"_行で分割...

2)map(. / ", ") ...とコンマ(Big gotcha:ご使用のバージョンでは、正規表現ベースの分割を使用する必要がありますこのように、引用符で囲まれたカンマで分割するからです。簡潔にするためにこれを使用しましたが、それによって私のソリューションはクールに見えますか?)

3)_if $doHeaders then..._ここでは、最初の行の要素の数と最初の行がヘッダー行かどうかに応じて、キーまたは数値の文字列の配列を作成します

4)_.[if $doHeaders then 1 else 0 end:]_わかりました。ヘッダーの場合、一番上の行を削除してください

5)map({($headers[.]): $values[.]})上記では、以前のcsvの各行を調べ、_$values_を変数に入れ、キーをパイプに入れました。次に、目的のオブジェクトを作成します。

もちろん、いくつかの正規表現を使って問題点を記入する必要がありますが、それが途中で始まることを願っています。

8
Tom

2018年以降、コードなしの最新のソリューションはPython tool csvkit has csvjson data.csv > data.json

ドキュメントを参照 https://csvkit.readthedocs.io/en/1.0.2/

このツールキットは、スクリプトがjqcsvの両方の形式をデバッグする必要がある場合にも、非常に便利でjsonを補完します。

visidata と呼ばれる強力なツールを確認することもできます。これが スクリーンキャストケーススタディ で、元のポスターに似ています。 visidataからスクリプトを生成することもできます

5
Michel Hua

以下は、-sおよび-Rオプションを指定してjqを実行することを想定したソリューションです。

[
  [                                               
    split("\n")[]                  # transform csv input into array
  | split(", ")                    # where first element has key names
  | select(length==3)              # and other elements have values
  ]                                
  | {h:.[0], v:.[1:][]}            # {h:[keys], v:[values]}
  | [.h, (.v|map(tonumber?//.))]   # [ [keys], [values] ]
  | [ transpose[]                  # [ [key,value], [key,value], ... ]
      | {key:.[0], value:.[1]}     # [ {"key":key, "value":value}, ... ]
    ]
  | from_entries                   # { key:value, key:value, ... }
]

サンプルの実行:

jq -s -R -f filter.jq data.csv

出力例

[
  {
    "name": "john",
    "age": 20,
    "gender": "male"
  },
  {
    "name": "jane",
    "age": 30,
    "gender": "female"
  },
  {
    "name": "bob",
    "age": 25,
    "gender": "male"
  }
]
2
jq170727