对象

对象 属于 jq

4 个练习

关于 对象

用其他语言的术语来说,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}

简写形式

有两种常见的对象构造场景有简写语法。

  1. 复用输入对象中的键值对。

    $ jq -n '$ENV | {USER: .USER, EDITOR: .EDITOR}'
    {
      "USER": "glennj",
      "EDITOR": "vim"
    }
    

    $ENV是一个对象,保存着环境变量的名称/值对。

    像key: .key这样重复的键值对,可以直接写成key

    $ jq -n '$ENV | {USER, EDITOR}'
    {
      "USER": "glennj",
      "EDITOR": "vim"
    }
    
  2. 同样,使用变量时也有简写。

    $ 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
    
通过 GitHub 编辑 该链接会在新窗口或标签页中打开

学习 对象

练习已锁定

再解锁 4 个练习即可练习 对象