用其他語言的術語來說,JSON 的物件就是「hash」、「map」或「字典」。
JSON 將_物件_定義為:
物件是一組無序的名稱/值配對。 物件以
{左大括號開頭,並以}右大括號結尾。 每個_名稱_後面接著:冒號,而_名稱/值_配對之間則以,逗號分隔。
名稱必須是字串。 名稱_的另一個說法是_鍵。
_值_可以是任何 JSON 型態。 同一個物件中不同的_值_可以有不同的型態,就像這個例子。
{
"name": "Jane",
"age": 42,
"pets": ["cat", "fish"],
"address": {"street": "123 Main St", "city": "Springfield"}
}
請注意,_最後_一組鍵值配對後面不能有逗號。
用大括號來收集名稱/值配對。 即使名稱必須是字串,只要名稱類似識別字(由英數字元和底線組成,且不以數字開頭),就不需要用引號括起來。
{name: "Jane", age: 42}
使用不_類似識別字_的鍵是合法的。 只要把它們加上引號就好。
{"first name": "Jane", "last name": "Lopez", age: 42}
如果名稱是某個運算式的結果,該運算式必須放在括號裡。
$ echo "Jane" | jq -Rc '{.: 42}'
jq: error: syntax error, unexpected '.' (Unix shell quoting issues?) at <top-level>, line 1:
{.: 42}
jq: error: May need parentheses around object key expression at <top-level>, line 1:
{.: 42}
jq: 2 compile errors
$ echo "Jane" | jq -Rc '{(.): 42}'
{"Jane":42}
物件建構有幾個常見情況可以用簡寫語法。
重複使用輸入物件中的鍵值配對。
$ jq -n '$ENV | {USER: .USER, EDITOR: .EDITOR}'
{
"USER": "glennj",
"EDITOR": "vim"
}
$ENV是一個保存環境名稱/值配對的物件。
重複出現的key: .key配對可以只寫成key。
$ jq -n '$ENV | {USER, EDITOR}'
{
"USER": "glennj",
"EDITOR": "vim"
}
同樣地,使用變數時也有一種簡寫。
$ jq -cn '"foo" as $a | "bar" as $b | {a: $a, b: $b}'
{"a":"foo","b":"bar"}
$ jq -cn '"foo" as $a | "bar" as $b | {$a, $b}'
{"a":"foo","b":"bar"}
值可以透過點記法從物件中取出。
{name: "Jane", age: 42} | .age # => 42
如果無法用識別字來表示鍵,就用方括號記法。
"name" as $key | {name: "Jane", age: 42} | .$key # => error
"name" as $key | {name: "Jane", age: 42} | .[$key] # => "Jane"
如果鍵不在物件裡,結果就是null。
{name: "Jane", age: 42} | .sport # => null
在解析 API 查詢的結果時,你會發現這是你必須反覆進行的一件事。
給定一個含有陣列或物件值的物件,我們可以把索引運算式「串接」在一起。
{
"name": "Jane",
"age": 42,
"pets": ["cat", "fish"],
"address": {"street": "123 Main St", "city": "Springfield"}
}
| (
.pets[1], # => "fish"
.address.city # => "Springfield"
)
要新增一組鍵值配對到陣列,或更新某個既有鍵的值,請使用=指定運算子,並在左邊放一個索引運算式。
{name: "Jane", age: 42} | .sport = "tennis" | .age = 21
# => {
# "name": "Jane",
# "age": 21,
# "sport": "tennis"
# }
+運算子會_合併_物件。
{Richard: 54} + {Jane: 42}
# => {
# "Richard": 54,
# "Jane": 42
# }
使用del函式來移除某個鍵。
它會回傳更新後的物件。
{name: "Jane", age: 42} | del(.age) # => {"name": "Jane"}
del的參數是一個索引運算式(使用點記法或方括號記法),它會解析成物件中的某個鍵。
jq稱它為路徑運算式。
只給一個字串是不夠的。
{name: "Jane", age: 42} | del(name) # error: name/0 is not defined
{name: "Jane", age: 42} | del("name") # error: Invalid path expression with result "name"
{name: "Jane", age: 42} | del(.name) # OK
{name: "Jane", age: 42} | del(.["name"]) # OK
要測試物件是否含有某個鍵,請使用has函式。
{name: "Jane", age: 42} as $example
|
($example | has("name")), # => true
($example | has("sport")) # => false
用in測試某個鍵是否在物件中。
{name: "Jane", age: 42} as $example
|
("name" | in($example)), # => true
("sport" | in($example)) # => false
使用keys函式來輸出所有鍵的陣列。
{name: "Jane", age: 42} | keys # => ["age", "name"]
請注意,keys會_排序_鍵。
若要依照原本的順序取得鍵,請使用keys_unsorted。
沒有對等的函式可以列出所有的_值_。
不過.[]篩選器會把物件的值輸出成一個_串流_,而這個串流可以用[...]陣列建構式捕捉起來。
[{first: "Jane", last: "Lopez", status: "awesome!"} | .[]]
# => ["Jane", "Lopez", "awesome!"]
map_values(filter)函式會把篩選器套用到物件裡的每個_值_。
{first: "Jane", last: "Lopez", status: "awesome!"}
| map_values(ascii_upcase)
# => {"first": "JANE", "last": "LOPEZ", "status": "AWESOME!"}
要疊代一個物件,我們必須先把它轉成一個由鍵值物件組成的陣列。
這件事由to_entries函式來做。
{name: "Jane", age: 42} | to_entries
# => [
# {
# "key": "name",
# "value": "Jane"
# },
# {
# "key": "age",
# "value": 42
# }
# ]
到了這裡,我們就可以使用陣列疊代函式,例如map。
from_entries函式則是反向操作:把由鍵值物件組成的陣列轉換成一個物件。
[
{"key":"name", "value":"Jane"},
{"key":"age", "value":42}
] | from_entries # => {"name": "Jane", "age": 42}
要對物件裡的_每一組_鍵值配對套用篩選器,請使用with_entries(filter)函式。
例如,給定一個把名稱對應到年齡的物件,鍵和值可以像下面這樣互換。
{"Jane": 42, "Richard": 54}
| with_entries({key: (.value | tostring), value: .key})
會輸出
{
"42": "Jane",
"54": "Richard"
}
with_entries(filter)等同於
to_entries | map(filter) | from_entries