JSON処理のコマンドラインツールであるjqは、JSONテキストを1行でちゃくっと解析、変換するときに使うものです。複雑なことをするときは、sed
やawk
など他のユーティリティを併用します。
とは言え、jq
にも変数定義などプログラミング言語らしき機能が備わっているので、複雑なことも「やればできんじゃねぇ?」と野望を募らせてしまうこともあります。
ここでは、次のようなオブジェクトを要素とした配列から、指定の部分文字列にName
の値がマッチするオブジェクトを抽出するjq
フィルタを考えます。
[
{
"id": 2,
"Name": "Cumings, Mrs. John Bradley (Florence Briggs Thayer)",
"Sex": "female",
"Age": 38,
"Ticket": "PC 17599",
"Fare": 71.2833,
"Cabin": "C85"
},
⋮
]
以下、入力データはファイルsearch-sample.json
に収容されているとします。
単純なマッチなら簡単です。contains
から指定のキー:値のプロパティが含まれているか(値は部分文字列でよい)の真偽値を得て、それをベースにselect
で真のオブジェクトだけを抜き出せばよいのです。
$ cat search-sample.json | jq --arg value Laina '.[] | select(contains({"Name": $value}))'
{
"id": 3,
"Name": "Heikkinen, Miss. Laina",
"Sex": "female",
"Age": 26,
"Ticket": "STON/O2. 3101282",
"Fare": 7.925,
"Cabin": ""
}
これが、値の大文字小文字に関わらずに検索せよとなると、多少ややこしくなります。私が頂いたオリジナルのお題は「Javaで書け」でしたが、相手はJSONテキストです。jq
でできないはずはないじゃないですか(というか、多分、jq
の方が楽だと思う)。
コード
コード(フィルタファイル)はsearch.jq
で、次のGithubから取得できます(短いのでコピペでも十分ですが)。
https://github.com/stoyosawa/jqDoc-public/
フィルタファイルの改行はUnixスタイルのLF
だけでなければならないので、保存時に注意してください。Windows流にCRLF
だと次のエラーが報告されます。
jq: error: syntax error, unexpected INVALID_CHARACTER (Unix shell quoting issues?)
at <top-level>, line 1:
フィルタファイルの使い方
これらファイルをjq
から呼び出すときは、次のオプションを指定します。
-
--arg
オプションー検索部分文字列を指定します。ここでは変数名をvalue
としたので、--arg value 文字列
となります。 -
-f
オプション(--from-file
)ーフィルタファイルを読み込みます。
まとめると、次のようになります(検索文字列はlaina)。
$ jq --arg value laina -f search.jq search-sample.json
大文字小文字を区別しないフィルタリング
フィルタは次の通りです。
($value | ascii_downcase) as $value | # 変数 value を小文字化
.[] | # オブジェクト単位のループ
.name = (.Name | ascii_downcase) | # 小文字版のname属性を加える
select(contains({"name": $value})) | # nameをベースにフィルタ
del(.name) # nameを削除
最初の大文字小文字を区別するワンライナーとの違いは、変数value
とプロパティName
の値を、どちらもascii_downcase
で小文字化しているところです(1、3行目)。そして、この小文字化された値を新しいプロパティname
としてオブジェクトに加えます(3行目)。
これで、contains
を用いた内包の真偽判定でname
の方を対象にフィルタリングすれば、大文字小文字を区別しない検索となります。
最後に一時的に用意したname
をdel
から削除します。
これで上がりです。
実行例を示します。検索文字列が小文字のlainaでもLainaが引っかかることがわかります。
$ cat search-sample.json | jq --arg value lai -f search.jq
{
"id": 3,
"Name": "Heikkinen, Miss. Laina",
"Sex": "female",
"Age": 26,
"Ticket": "STON/O2. 3101282",
"Fare": 7.925,
"Cabin": ""
}
おわりに
実用性のない、お遊びなjq
フィルタを追及していますが、この回は、実用性ありですね。残念。でも、Unixユーザならegrep
を使います、きっと。
$ cat search-sample.json | jq -c '.[]' | egrep -i '"Name":".+laina' | jq '.'
ポイントは、-c
オプションで複数のプロパティからなるオブジェクトを1行にまとめることで、行志向のgrep
でオブジェクト全体を引っかけられるようにしているところです。末尾のjq
はこの1行化されたオブジェクトを、読みやすいように1プロパティ1行に戻しています。正規表現は、これだとName
以降のプロパティ値にLainaがあったときにも引っかかってしまいます(たとえば "Ticket": "Laina")ので、再考の余地があります。
参考
-
./jq -
jq
のオフィシャルサイトです(英文)。 -
man page for GREP - Unixコマンドの
egrep
のman pageです。 -
jqマニュアル(開発バージョン) - Yuji Okazawa氏が(非公式に)訳しておられる
jq
マニュアルの日本語版です。和訳マニュアルは他にもあるので、検索してください。 - The JavaScript Object Notation (JSON) Data Interchange Format(RFC 8259) - JSONの仕様です(英文)。しばしばRFC 7159が参照されますが、そちらはobsoleteになりました。
- JSON(JavaScript Object Notation)データ交換フォーマット - 上記の非公式の和訳です。
-
jqハンドブックーNetOps/DevOps必携のJSONパーザ -
contain
やselect
はこちらでも取り上げています。【出版社 | honto | amazon.co.jp | ヨドバシカメラ】。