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 | ヨドバシカメラ】。
