JSONのオブジェクトは、他の言語の用語で呼ぶなら「ハッシュ」「マップ」「辞書」にあたります。
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