"jq 是一個輕巧又靈活的命令列 json 處理器。"
jq 之於 json, 就像
regexp 之於純文字檔, 或像是
QueryPath 之於 html/xml 檔, 可以擷取/過濾/篩選/批次編輯 json 檔。。
它的學習難度介於下指令跟寫程式之間。
[2022/5/29] 另一個類似工具 zq 的語法更容易理解; 但有些時候 jq 還是比較好用。
jq 是比較新的軟體, 所以標準的 debian wheezy (7.* 系列) 沒有收錄到它。 可以在 /etc/apt/sources.list 裡面加一列:
deb http://opensource.nchc.org.tw/debian/ wheezy-backports main contrib然後就可以
apt-get update ; apt-get install jq
把它安裝起來。
接著下載
data.tgz 並解壓縮, 隨便抓一個 json 檔來測試。
這些是當初我剛開始學寫 osm 程式、 還不知道什麼叫做 geojson 格式的時候,
隨便胡亂產生的 json 檔。 這一篇的目標是要把這些檔轉成 geojson 格式,
並且在過程當中順便學會使用 jq。
請仔細觀察每一步新增的 jq 指令有何效果。
[2025/8/4] 用 '[]' 可能會比 'map()' 更好讀。 有空再來改以下。 大推 這篇
jq '.' airports.json
把輸入資料按照層次縮格排版、 內容原封不動印出來。 可以當做 json lint 或 pretty-printer 來用。jq '.[0:3]' airports.json
因為輸入資料是一個陣列, 所以可以用 [0:3] 取得最前面 (編號 0、 1、 2 的) 三個元素。jq '.[-2:]' airports.json
抓出最後面兩個元素。jq '.[]' airports.json
抓出所有元素。 (最終效果跟第一句一樣。)jq '.[].name' airports.json
抓出所有元素的 name 欄位。jq '[ .[].name ]' airports.json
同上, 但把結果放進一個 json 陣列。jq 'map( . )' airports.json
對陣列裡的每個元素都進行 "." 動作 (把輸入資料原封不動印出來)。 最終效果跟第一句一樣。jq 'map( .name )' airports.json
對陣列裡的每個元素都進行 ".name" 動作 (抓出 name 欄位)。 最終效果跟第六句一樣。jq 'map( { name:.name } )' airports.json
對陣列裡的每個元素都進行 "{ name:.name }" 動作 -- 建立一個 json hash, 裡面只有一個 name 欄位。jq 'map( { properties: { name:.name } } )' airports.json
類似上一句, 但每個元素轉換完後, 外面再包一層 hash。jq 'map( { type: "Feature", geometry: { type:"Point" }, properties: { name:.name } } )' airports.json
類似上一句, 但每個元素轉換完後所變成的每一個 hash, 又多出了幾個欄位。jq 'map( { type: "Feature", geometry: { type:"Point", coordinates:[.lon, .lat] }, properties: { name:.name } } )' airports.json
把經緯度座標補上去。 至此, 你可以將輸出存檔 (... > airports.geojson) 並將這個檔案 上傳到 umap 去。- 但是 mapbbcode 對上傳檔案的格式要求比較嚴格。
外面還要再包一層標準的表頭:
jq 'map( { type: "Feature", geometry: { type:"Point", coordinates:[.lon, .lat] }, properties: { name:.name } } )' airports.json | jq '{ type:"FeatureCollection", title:"機場", features: . }' > ~/airports.geojson
也就是在上一句後面加一個 pipe 再呼叫 jq 外面再包一層並存檔。 這個檔就可以 上傳到 mapbbcode 了。
一些心得與提醒:
- 其實最後一步並不需要另外再呼叫一次 jq。 可以直接把最外面包的那一層直接寫到原先的 jq 指令, 如插圖左上角程式碼。
- 反過來說, 第十句也可以改成第九句再加一個 pipe、 第十一句也可以改成第十句再加一個 pipe、 ...
- 對我而言, map 超級好用。 一般很常遇到需要手工處理的 .json 資料檔 (特別是座標檔) 都是一個大陣列。 用了 map 之後, 在寫裡面的算式時, 心理就只需要想著一個元素, 而不必想整個陣列。
- 處理 geojson 時很好用的句型:
jq '.features | map(...)' 例如 jq '.features | map([.geometry.type, .properties.name])' data.geojson
- jq 還有很多強大功能。 有空再來研究 手冊。
我為什麼會找到 jq? 就是為了測試 umap 跟 mapbbcode, 需要把先前的舊資料轉檔。 第一個想法是: 可以用 perl 的 JSON 模組。 不過... 難道沒有人寫好現成的命令列工具嗎? 一搜尋 「json command line」 就看到 jq 啦! 懶惰是資訊人的美德之一, 下次要寫通用程式之前, 記得先搜尋, 可能會跟我一樣經常有很多意外的收穫哦!
(本文接受科技部計畫補助: MOST-103-2221-E-492-028 「台灣開放街圖圖資平台建置與應用」)
沒有留言:
張貼留言
因為垃圾留言太多,現在改為審核後才發佈,請耐心等候一兩天。