Обʼєкт JSON, якщо скористатися термінологією з інших мов, - це «хеш», «мапа» або «словник».
JSON визначає обʼєкт так:
Обʼєкт - це невпорядкована множина пар імʼя/значення. Обʼєкт починається з
{лівої фігурної дужки і закінчується}правою фігурною дужкою. Після кожного імені стоїть:двокрапка, а пари імʼя/значення відокремлюються,комою.
Імʼя обовʼязково має бути рядком тексту (англ. string). Інше слово для імені - ключ.
Значення може бути будь-якого типу 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