jq 簡介
jq
是一個常用的指令工具,用於處理 JSON 格式的資料,輕量但功能強大,可以做到從 JSON 資料中擷取、轉換、篩選等操作,也可以簡單用於排版讓 JSON 資料更可讀。
這邊文章筆記常用的 jq
功能和範例。
安裝 jq
macOS (by homebrew)
brew list jq
Linux
## for Ubuntu/Debian:
sudo apt-get update
sudo apt-get install jq
## for CentOS/Red Hat:
sudo yum install jq
排版
範例資料 example1.json
❯ $ cat example1.json
{"name":"John",
"age":18}
增加人眼可讀性 – pretty-printed
$ cat example1.json | jq # 或 `cat example1.json | jq .` 亦可
{
"name": "John",
"age": 18
}
壓縮資料 – compact
濾掉多餘的換行、空白:
$ cat example1.json | jq -c
{"name":"John","age":18}
序列化/反序列化(Serialization/Deserialization)
不同作業系統或程式語言,可能使用不同的內部表示方式,為了解決資料互通問題,所以需要序列化。序列化(Serialization)是將資料結構或物件,轉換成可被解讀的格式,可以存儲在檔案中或者在網絡上傳輸,在不同系統或環境之間互通。
JSON 序列化後會是一段字串,jq 中也提供了 2 個跟序列化/反序列化有關的參數:
Option | Description |
---|---|
-R |
序列化 (將 JSON 格式轉成可以傳輸的字串) |
-r |
反序列化 (將字串轉回 JSON 格式) |
序列化 (將 JSON 格式轉成可以傳輸的字串)
## 範例資料
❯ $ cat example1.json
{"name":"John",
"age":18}
## 序列化用法
❯ $ cat example1.json | jq -R
"{\"name\":\"John\","
"\"age\":18}"
## 小技巧:如果想要緊實美觀一點,可以先壓縮再序列化
❯ $ cat example1.json | jq -c | jq -R
"{\"name\":\"John\",\"age\":18}"
反序列化 (將字串轉回 JSON 格式)
## 序列化過的範例資料
❯ $ cat example_serialization.txt
"{\"name\":\"John\",\"age\":18}"
## 如果沒有先反序列化,直接解析沒用,只會被當普通字串
❯ $ cat example_serialization.txt | jq
"{\"name\":\"John\",\"age\":18}"
## 反序列化用法
❯ $ cat example_serialization.txt | jq -r
{"name":"John","age":18}
## 小技巧:反序列化再排版
❯ $ cat example_serialization.txt | jq -r | jq
{
"name": "John",
"age": 18
}
取出特定欄位的值
範例資料 example2.json
❯ $ cat example2.json
{"name": "John",
"age": 18,
"job": null,
"skills": ["Java", "C#", "Node"],
"address":{"country": "Taiwan", "city": "Taipie", "district": "Xinyi"}}
取得某一個 key 的值
兩種寫法都可以:
❯ $ cat example2.json | jq .name
❯ $ cat example2.json | jq '.["name"]'
"John"
如果 key 不存在,會回 null:
❯ $ cat example2.json | jq .aaa
null
如果 key 存在但 value 是 null,也是回傳 null:
❯ $ cat example2.json | jq .job
null
取得多個 key 的值
❯ $ cat example2.json | jq '.name, .age, .skills, .aaa'
❯ $ cat example2.json | jq '.["name", "age", "skills", "aaa"]'
"John"
18
[
"Java",
"C#",
"Node"
]
null
可以把結果包成一個陣列,方便後續繼續做更多處理:
❯ $ cat example2.json | jq '[.["name", "age", "skills", "aaa"]]'
[
"John",
18,
[
"Java",
"C#",
"Node"
],
null
]
取出第二層以上的欄位值
這兩種寫法都可以:
❯ $ cat example2.json | jq .address.city
❯ $ cat example2.json | jq '.["address"]["city"]'
"Taipie"
小技巧:取出字串值並清理雙引號
❯ $ cat example2.json | jq .name
"John"
❯ $ cat example2.json | jq .name | sed 's/"//g'
John
過濾 JSON object 保留特定欄位
❯ $ cat example2.json | jq '{name, skills}'
{
"name": "John",
"skills": [
"Java",
"C#",
"Node"
]
}
取得第一層所有 value
如果資料是物件 (Object)
基本用法:
❯ $ cat example2.json | jq '.[]'
"John"
18
null
[
"Java",
"C#",
"Node"
]
{
"country": "Taiwan",
"city": "Taipie",
"district": "Xinyi"
}
將結果用陣列包起來:
❯ $ cat example2.json | jq [.[]]
[
"John",
18,
null,
[
"Java",
"C#",
"Node"
],
{
"country": "Taiwan",
"city": "Taipie",
"district": "Xinyi"
}
]
如果資料是陣列 (Array)
## 範例資料
❯ $ cat example3_array.json
[{"name": "Rubio","age": 32}, {"name": "Jokic","age": 28}]
## 效果
❯ $ cat example3_array.json | jq .[]
{
"name": "Rubio",
"age": 32
}
{
"name": "Jokic",
"age": 28
}
取得第一層所有 key
如果資料是物件 (Object)
會自動用一個陣列包起來:
❯ $ cat example2.json | jq keys
[
"address",
"age",
"job",
"name",
"skills"
]
如果資料是陣列 (Array)
陣列的 key 只是流水號,意義不大:
## 範例資料
❯ $ cat example3_array.json
[{"name": "Rubio","age": 32}, {"name": "Jokic","age": 28}]
## 效果
❯ $ cat example3_array.json | jq keys
[
0,
1
]
陣列操作 – 擷取元素 (Element)
範例資料 example4_array.json
❯ $ cat example4_array.json
[{"name":"person1","age":11},{"name":"person2","age":22},{"name":"person3","age":33},{"name":"person4","age":44}]
擷取單一元素
指定陣列元素的 index:
❯ $ cat example4_array.json | jq .[1]
{
"name": "person2",
"age": 22
}
如果擷取到不存在的 index,回傳 null:
❯ $ cat example4_array.json | jq .[10]
null
擷取多個元素
可以指定多個 index:
❯ $ cat example4_array.json | jq .[1,3]
{
"name": "person2",
"age": 22
}
{
"name": "person4",
"age": 44
}
或是指定一個 index 範圍,注意這裡列印的結果已經有用陣列包起來:
❯ $ cat example4_array.json | jq .[1:3] # 不含 element[3]
[
{
"name": "person2",
"age": 22
},
{
"name": "person3",
"age": 33
}
]
印出所有元素
❯ $ cat example4_array.json | jq .[]
{
"name": "person1",
"age": 11
}
{
"name": "person2",
"age": 22
}
{
"name": "person3",
"age": 33
}
{
"name": "person4",
"age": 44
}
陣列操作 – 擷取元素 (Element) 中的特定欄位 value
取特定一個欄位
以下 3 種寫法都可以:
❯ $ cat example4_array.json | jq .[].name
❯ $ cat example4_array.json | jq '.[]["name"]'
❯ $ cat example4_array.json | jq '.[]|.name'
"person1"
"person2"
"person3"
"person4"
如果想將結果包成新的陣列:
❯ $ cat example4_array.json | jq '[.[].name]'
❯ $ cat example4_array.json | jq '[.[]["name"]]'
❯ $ cat example4_array.json | jq '[.[]|.name]'
[
"person1",
"person2",
"person3",
"person4"
]
也可以用類似過濾的方式,將結果包成新的物件陣列:
❯ $ cat example4_array.json | jq '.[]|={name}'
[
{
"name": "person1"
},
{
"name": "person2"
},
{
"name": "person3"
},
{
"name": "person4"
}
]
取多個欄位
基本用法,單純逐一印出欄位的 value:
❯ $ cat example4_array.json | jq '.[]|.name, .age'
"person1"
11
"person2"
22
"person3"
33
"person4"
44
上面寫法即使用陣列包起來,來自同一個 element 的 value 並不會 group,如果要額外處理並不好處理:
❯ $ cat example4_array.json | jq '[.[]|.name, .age]'
[
"person1",
11,
"person2",
22,
"person3",
33,
"person4",
44
]
如果希望來自同一個 element 的 value 有 group 效果,可以這樣寫:
❯ $ cat example4_array.json | jq '.[]|[.name, .age]'
[
"person1",
11
]
[
"person2",
22
]
[
"person3",
33
]
[
"person4",
44
]
陣列操作 – 擷取元素 (Element) 中的特定欄位 value 並作字串加工
以下 3 種寫法都可以:
❯ $ cat example4_array.json | jq '"Hi " + .[]["name"]'
❯ $ cat example4_array.json | jq '"Hi " + (.[]["name"])'
❯ $ cat example4_array.json | jq '"Hi \(.[]["name"])"' # (注意要用雙引號將整個 expression 包圍起來)
"Hi person1"
"Hi person2"
"Hi person3"
"Hi person4"
map() – 對陣列元素逐一操作
jq 支援一些函數功能,其中 map() 函數可以對 Array 的每一項 Element 逐一操作,最後合併結果,在某些場景應用很強大。
取出陣列中的特定 key,將結果包成陣列
這和上面的 cat example4_array.json | jq '[.[].name]'
效果是類似的,只是用 map() 的語法更簡潔易懂:
❯ $ cat example4_array.json | jq 'map(.name)'
[
"person1",
"person2",
"person3",
"person4"
]
字串加工
❯ $ cat example4_array.json | jq 'map("Hi " + .name)'
[
"Hi person1",
"Hi person2",
"Hi person3",
"Hi person4"
]
搭配 if-else 的範例
可以做更豐富的判斷應用,例如:
❯ $ echo '[0, 1, 2, 3]' | jq 'map(if . == 0 then "零" elif . == 1 then "壹" elif . == 2 then "貳" else "其他" end)'
[
"零",
"壹",
"貳",
"其他"
]
length – 取得字串或陣列的長度
length 可以用來取得長度:
- 字串: 字元長度
- 陣列: element 數
取得字串長度
❯ $ echo '{"url":"onejar99.com", "name":"OneJar 的隧道"}' | jq '.url|length'
12
取得陣列長度
❯ $ cat example4_array.json
[{"name":"person1","age":11},{"name":"person2","age":22},{"name":"person3","age":33},{"name":"person4","age":44}]
❯ $ cat example4_array.json | jq '.|length'
4
split() – 切割字串
❯ $ echo '"Rubio,Jokic,Jeremy"' | jq 'split(",")'
[
"Rubio",
"Jokic",
"Jeremy"
]
join() – 將陣列元素組合成一個字串
$ echo '["Rubio" ,"Jokic" ,"Jeremy"]' | jq '.|join(",")'
"Rubio,Jokic,Jeremy"
select() – 條件過濾
select() 常常搭配 map() 使用,做到動態條件過濾,只列印符合條件的 element。
範例資料 example5_array.json
❯ $ cat example5_array.json
[{"name":"Rubio","age":32},{"name":"Jokic","age":28},{"name":"Jeremy","age":34}]
條件示範:過濾掉 name 過長的人 (字串欄位)
❯ $ cat example5_array.json | jq 'map(select(.name|length <= 5))'
[
{
"name": "Rubio",
"age": 32
},
{
"name": "Jokic",
"age": 28
}
]
條件示範:過濾留下 age 大於 30 歲的人 (數字欄位)
❯ $ cat example5_array.json | jq 'map(select(.age|. > 30))'
[
{
"name": "Rubio",
"age": 32
},
{
"name": "Jeremy",
"age": 34
}
]
小技巧:取出字串值並清理雙引號
❯ $ cat example2.json | jq .name
“John”
❯ $ cat example2.json | jq .name | sed ‘s/”//g’
John
这个可以用
cat example2.json | jq .name -r
即可
這篇文章很好地介紹了 jq 工具的基本使用,讓我更深入了解如何有效處理 JSON。期待更多類似的實用教學!