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