JSONとは
JSONは、データを構造化して保存および交換するための軽量なフォーマットです。
Javascriptと親和性が高いようです。
以下2つのデータ構造が基本となっています。
1. オブジェクト(Object)
- {}で囲まれたもの
- キーと値のペアを保持
- キーは文字列、値にはさまざまなデータ型が使用可能
2. 配列(Array)
- []で囲まれたもの
- 値のリスト形式で保持
- 値の順序が重要な場合に使用される
今回は私が作った以下の適当なデータ(sample.json)を使ってコマンドをまとめていきます。
※登場人物はフィクションであり実在のものとは一切関係ありません。
{
"Personal":[
{
"Name":"Smith John",
"Age":"25",
"Gender":"Male",
"Birthday":"2000/04/25",
"PhoneNumber":"090-12x-xx01",
"Address":[
{
"PostalCode":"90028",
"City":"Los Angeles"
}
],
"ID":"001"
},
{
"Name":"Smith Kevin",
"Age":"23",
"Gender":"Female",
"Birthday":"2002/03/18",
"PhoneNumber":"090-13x-xx01",
"Address":[
{
"PostalCode":"90028",
"City":"Los Angeles"
}
],
"ID":"002"
}
]
}
jqとは
jqとは、JSONを操作することができるツールです。
上記のようなJSONデータをきれいに整形したり必要なデータだけ抽出したりできます。
標準出力
引数.は、{から、}で囲われている部分を表します。
出力結果はcatと変わりません。
cat ./sample.json | jq '.'
{
"Personal":[
{
"Name":"Smith John",
"Age":"25",
"Gender":"Male",
"Birthday":"2000/04/25",
"PhoneNumber":"090-12x-xx01",
"Address":[
{
"PostalCode":"90028",
"City":"Los Angeles"
}
],
"ID":"001"
},
{
"Name":"Smith Kevin",
"Age":"23",
"Gender":"Female",
"Birthday":"2002/03/18",
"PhoneNumber":"090-13x-xx01",
"Address":[
{
"PostalCode":"90028",
"City":"Los Angeles"
}
],
"ID":"002"
}
]
}
配列を指定した出力
インデックスを指定して抽出できます。(0から始まります)
cat ./sample.json | jq '.Personal[0]'
{
"Name":"Smith John",
"Age":"25",
"Gender":"Male",
"Birthday":"2000/04/25",
"PhoneNumber":"090-12x-xx01",
"Address":[
{
"PostalCode":"90028",
"City":"Los Angeles"
}
],
"ID":"001"
}
範囲を指定した出力
[0:2]では、0以上2未満で抽出されます。
範囲の開始を指定しない[:2]の場合は、0から開始されます。 ※[0:2]と同義
cat ./sample.json | jq '.Personal[0:2]'
[
{
"Name":"Smith John",
"Age":"25",
"Gender":"Male",
"Birthday":"2000/04/25",
"PhoneNumber":"090-12x-xx01",
"Address":[
{
"PostalCode":"90028",
"City":"Los Angeles"
}
],
"ID":"001"
},
{
"Name":"Smith Kevin",
"Age":"23",
"Gender":"Female",
"Birthday":"2002/03/18",
"PhoneNumber":"090-13x-xx01",
"Address":[
{
"PostalCode":"90028",
"City":"Los Angeles"
}
],
"ID":"002"
}
]
後方から範囲指定する場合は[-1:]として抽出することも可
cat ./sample.json | jq '.Personal[-1:]'
[
{
"Name":"Smith Kevin",
"Age":"23",
"Gender":"Female",
"Birthday":"2002/03/18",
"PhoneNumber":"090-13x-xx01",
"Address":[
{
"PostalCode":"90028",
"City":"Los Angeles"
}
],
"ID":"002"
}
]
最後尾から出力
配列のインデックスをreverseして抽出できます。
cat ./sample.json | jq '.[] | reverse[]'
{
"Name":"Smith John",
"Age":"25",
"Gender":"Male",
"Birthday":"2000/04/25",
"PhoneNumber":"090-12x-xx01",
"Address":[
{
"PostalCode":"90028",
"City":"Los Angeles"
}
],
"ID":"001"
},
{
"Name":"Smith Kevin",
"Age":"23",
"Gender":"Female",
"Birthday":"2002/03/18",
"PhoneNumber":"090-13x-xx01",
"Address":[
{
"PostalCode":"90028",
"City":"Los Angeles"
}
],
"ID":"002"
}
値の出力
各配列にある値を抽出する場合は、JSONデータ構造を意識して抽出しましょう。
.[ ]をつけることで[ ]が取れるイメージです。
cat ./sample.json | jq '.Personal[].Name'
"Smith John"
"Smith Kevin"
|や.は省略して書ける部分もあります。
jq '.Personal[ ].Name' = jq '.Personal | .[ ] | .Name'
値の出力(フィルタリング)
select
jqには、さまざまな組み込み演算子や関数が用意されています。
特定の値だけ抽出する場合は、selectを使ってフィルタリングできます。
Gender値がMaleと一致する配列からGender値を抽出する場合は以下
cat ./sample.json | jq '.Personal[] | select( .Gender == "Male") | .Gender'
"Male"
startswith
090から始まるPhoneNumber値を抽出する場合は以下
cat ./sample.json | jq '.Personal[] | select( .PhoneNumber | startswith ("090")) | .PhoneNumber'
"090-12x-xx01"
"090-13x-xx01"
endswith
01で終わるPhoneNumber値を抽出する場合は以下
cat ./sample.json | jq '.Personal[] | select( .PhoneNumber | endswith ("01")) | .PhoneNumber'
"090-12x-xx01"
"090-13x-xx01"
and
090から始まる、かつ01で終わるPhoneNumber値を抽出する場合は以下
cat ./sample.json | jq '.Personal[] | select( .PhoneNumber | startswith ("090") and endswith ("01")) | .PhoneNumber'
"090-12x-xx01"
"090-13x-xx01"
or
090から始まる、もしくは01で終わるPhoneNumber値を抽出する場合は以下
cat ./sample.json | jq '.Personal[] | select( .PhoneNumber | startswith ("090") or endswith ("01")) | .PhoneNumber'
"090-12x-xx01"
"090-13x-xx01"
contains
Name値にSmithを含む配列からName値を抽出する場合は以下
cat ./sample.json | jq '.Personal[] | select( .Name | contains ( "Smith")) | .Name'
"Smith John"
"Smith Kevin"
出力結果を加工する
フィルタリングした内容を加工することも可能です。
先ほどの出力だと値だけ出力されますが、{ 任意のキー: 値 }を追加して出力することができます。
cat ./sample.json | jq '.Personal[] | select( .PhoneNumber | startswith ("090") and endswith ("01")) | { PhoneNumber: .PhoneNumber }'
{
"PhoneNumber":"090-12x-xx01"
}
{
"PhoneNumber":"090-13x-xx01"
}
ちなみにカンマで区切ればいくつも追加出力可能
cat ./sample.json | jq '.Personal[] | select( .PhoneNumber | startswith ("090") and endswith ("01")) | { Name: .Name, PhoneNumber: .PhoneNumber }'
{
"Name":"Smith John",
"PhoneNumber":"090-12x-xx01"
}
{
"Name":"Smith Kevin",
"PhoneNumber":"090-13x-xx01"
}
""無しで出力したい場合
jqはJSON形式で出力するためダブルクォーテーションにて括られたで形でデータが出力されます。
オプション-rを追加すると""無しで出力されます。
cat ./sample.json | jq -r '.Personal[] | select( .PhoneNumber | startswith ("090") and endswith ("01")) | .PhoneNumber'
090-12x-xx01
090-13x-xx01
以下のような関数も使用可
select関数
値に対してとある関数を適用したとき、結果が真(TRUE)の場合は元の値をそのまま出力します。
結果が偽(FALSE)の場合、元の値は捨てられ空の出力を行います。(何も表示されません)
$ echo -1000 | jq 'select(. >= 0 and . < 100)'
$ echo 10 | jq 'select(. >= 0 and . < 100)'
10
$ echo '[3,-100,10,1000,18]' | jq -c '[.[] | select(. >= 0 and . < 100)]'
[3,10,18]
map関数
$ echo '[3,10,18]' | jq -c 'map(. + 10)'
[13,20,28]
最後に
出力結果をcsvにしてアウトプットするとかなり便利です。
cat ./sample.json | jq -r '.Personal[] | [.Name, .Age, .Gender] | @csv' > sample01.csv
参考