2015年3月18日 星期三

json 轉檔萬用瑞士刀 jq

用 jq 把一個普通的 json 檔轉成 geojson 格式 "jq 是一個輕巧又靈活的命令列 json 處理器。" jq 之於 json, 就像 regexp 之於純文字檔, 或像是 QueryPath 之於 html/xml 檔, 可以擷取/過濾/篩選/批次編輯 json 檔。。 它的學習難度介於下指令跟寫程式之間。

[2022/5/29] 改推語法更容易理解的 zq

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 指令有何效果。
  1. jq '.' airports.json 把輸入資料按照層次縮格排版、 內容原封不動印出來。 可以當做 json lint 或 pretty-printer 來用。
  2. jq '.[0:3]' airports.json 因為輸入資料是一個陣列, 所以可以用 [0:3] 取得最前面 (編號 0、 1、 2 的) 三個元素。
  3. jq '.[-2:]' airports.json 抓出最後面兩個元素。
  4. jq '.[]' airports.json 抓出所有元素。 (最終效果跟第一句一樣。)
  5. jq '.[].name' airports.json 抓出所有元素的 name 欄位。
  6. jq '[ .[].name ]' airports.json 同上, 但把結果放進一個 json 陣列。
  7. jq 'map( . )' airports.json 對陣列裡的每個元素都進行 "." 動作 (把輸入資料原封不動印出來)。 最終效果跟第一句一樣。
  8. jq 'map( .name )' airports.json 對陣列裡的每個元素都進行 ".name" 動作 (抓出 name 欄位)。 最終效果跟第六句一樣。
  9. jq 'map( { name:.name } )' airports.json 對陣列裡的每個元素都進行 "{ name:.name }" 動作 -- 建立一個 json hash, 裡面只有一個 name 欄位。
  10. jq 'map( { properties: { name:.name } } )' airports.json 類似上一句, 但每個元素轉換完後, 外面再包一層 hash。
  11. jq 'map( { type: "Feature", geometry: { type:"Point" }, properties: { name:.name } } )' airports.json 類似上一句, 但每個元素轉換完後所變成的每一個 hash, 又多出了幾個欄位。
  12. jq 'map( { type: "Feature", geometry: { type:"Point", coordinates:[.lon, .lat] }, properties: { name:.name } } )' airports.json 把經緯度座標補上去。 至此, 你可以將輸出存檔 (... > airports.geojson) 並將這個檔案 上傳到 umap 去
  13. 但是 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 了。

一些心得與提醒:

  1. 其實最後一步並不需要另外再呼叫一次 jq。 可以直接把最外面包的那一層直接寫到原先的 jq 指令, 如插圖左上角程式碼。
  2. 反過來說, 第十句也可以改成第九句再加一個 pipe、 第十一句也可以改成第十句再加一個 pipe、 ...
  3. 對我而言, map 超級好用。 一般很常遇到需要手工處理的 .json 資料檔 (特別是座標檔) 都是一個大陣列。 用了 map 之後, 在寫裡面的算式時, 心理就只需要想著一個元素, 而不必想整個陣列。
  4. 處理 geojson 時很好用的句型jq '.features | map(...)' 例如 jq '.features | map([.geometry.type, .properties.name])' data.geojson
  5. jq 還有很多強大功能。 有空再來研究 手冊

我為什麼會找到 jq? 就是為了測試 umap 跟 mapbbcode, 需要把先前的舊資料轉檔。 第一個想法是: 可以用 perl 的 JSON 模組。 不過... 難道沒有人寫好現成的命令列工具嗎? 一搜尋 「json command line」 就看到 jq 啦! 懶惰是資訊人的美德之一, 下次要寫通用程式之前, 記得先搜尋, 可能會跟我一樣經常有很多意外的收穫哦!

(本文接受科技部計畫補助: MOST-103-2221-E-492-028 「台灣開放街圖圖資平台建置與應用」)

沒有留言:

張貼留言

因為垃圾留言太多,現在改為審核後才發佈,請耐心等候一兩天。