如何使用jq有条件地将值映射到新对象

时间:2018-02-13 09:05:51

标签: json jq

我从DigitalOcean JSON API获取Droplet列表,并使用jq map将响应转换为我需要的信息。

我希望能够添加一个可选键和&值对应我的新对象,基于响应是否包含具有特定值的键。

如果响应不包含匹配项,则使用select()时,整个Droplet条目将从我的新对象中删除,而不仅仅是键。

如何过滤单个键而不是整个条目?

详细说明:

单个液滴的响应如下所示:

 "id": 12345678,
 "name": "my-droplet",
 ...
 "networks": {
      "v4": [
        {
          "ip_address": "123.456.78.90",
          "netmask": "255.255.240.0",
          "gateway": "123.123.0.1",
          "type": "public"
        },
        {
          "ip_address": "10.123.45.67",
          "netmask": "255.255.0.0",
          "gateway": "10.123.0.1",
          "type": "private"
        }
      ],
      "v6": []
    },
 ...

我想将其转换为以下形式的对象:

{
  "id": 12345678,
  "name": "my-droplet",
  "public_ip": "123.456.78.90",
  "private_ip" "10.123.45.67"
} 

所有水滴都有公共IP,但私有IP是可选的。

我有这个,忽略了可选的私有IP,到目前为止工作正常:

jq '.droplets | map({id: .id, name: .name, status: .status, public_ip: .networks.v4[] | select(.type=="public") | .ip_address})'

但是,一旦我添加私有IP,任何没有一个的Droplet都会从响应中丢失:

jq '.droplets | map({id: .id, name: .name, status: .status, public_ip: .networks.v4[] | select(.type=="public") | .ip_address, private_ip: .networks.v4[] | select(.type=="private") | .ip_address})'

我认为我需要使用串联或条件,但我无法弄清楚语法。

注意:我发现了类似的问题,但它并未包含更改密钥的名称:denormalizing JSON with jq

1 个答案:

答案 0 :(得分:1)

如果没有私人IP(或者确实存在多个私人IP),您没有准确指出应该发生什么,但以下内容应该让您继续前进。

.droplets
| map({id, name, status, 
       public_ip: (first(.networks.v4[] | select(.type=="public"))  // {})
                  | .ip_address,
       private_ip: (first(.networks.v4[] | select(.type=="private")) // {})
                  | .ip_address } )

条件键

def one(condition): first(.[] | select(condition)) // null;

.droplets
| map({id, name, status}
      + (.networks.v4
         | { public_ip: one(.type=="public") | .ip_address}
           + (one(.type=="private") 
              | if . then {private_ip: .ip_address} else null end ) ))