使用jq在JSON中添加或更新数组中的对象

时间:2017-06-06 23:30:31

标签: json jq

我尝试使用jq来解析JSON(实际上是OpenShift oc process ...命令的输出),并且添加/更新 env 具有新键/值对的container数组。

示例输入

{
  "kind": "List",
  "apiVersion": "v1",
  "metadata": {},
  "items": [
    {
      "apiVersion": "v1",
      "kind": "Service",
      "metadata": {
        "annotations": {
          "description": "Exposes and load balances the node.js application pods"
        },
        "name": "myapp-web"
      },
      "spec": {
        "ports": [
          {
            "name": "web",
            "port": 3000,
            "protocol": "TCP",
            "targetPort": 3000
          }
        ],
        "selector": {
          "name": "myapp"
        }
      }
    },
    {
      "apiVersion": "v1",
      "kind": "Route",
      "metadata": {
        "name": "myapp-web"
      },
      "spec": {
        "host": "app.internal.io",
        "port": {
          "targetPort": "web"
        },
        "to": {
          "kind": "Service",
          "name": "myapp-web"
        }
      }
    },
    {
      "apiVersion": "v1",
      "kind": "DeploymentConfig",
      "metadata": {
        "annotations": {
          "description": "Defines how to deploy the application server"
        },
        "name": "myapp"
      },
      "spec": {
        "replicas": 1,
        "selector": {
          "name": "myapp"
        },
        "strategy": {
          "type": "Rolling"
        },
        "template": {
          "metadata": {
            "labels": {
              "name": "myapp"
            },
            "name": "myapp"
          },
          "spec": {
            "containers": [
              {
                "env": [
                  {
                    "name": "A_ENV",
                    "value": "a-value"
                  }
                ],
                "image": "node",
                "name": "myapp-node",
                "ports": [
                  {
                    "containerPort": 3000,
                    "name": "app",
                    "protocol": "TCP"
                  }
                ]
              }
            ]
          }
        },
        "triggers": [
          {
            "type": "ConfigChange"
          }
        ]
      }
    }
  ]
}

在这个JSON中,我想做以下事情:

  • 找到DeploymentConfig对象
  • 检查第一个容器中是否有env数组
  • 如果是,请在其中添加新对象{"name": "B_ENV", "value": "b-value"}
  • 如果没有,请添加env 数组,其中包含对象{"name": "B_ENV", "value": "b-value"}

到目前为止,我能够解决部分问题,我能够找到相关对象,并将新的env var添加到容器中:

oc process -f <dc.yaml> -o json | jq '.items | map(if .kind == "DeploymentConfig" 
    then .spec.template.spec.containers[0].env |= .+ [{"name": "B_ENV", "value": "b-value"}] 
    else . 
    end)'

这可以按预期插入新的env var,但输出是一个数组,如下所示。 此外,它根本不处理env数组可能不存在的部分。

我希望能够生成与输入相同的输出,但添加了新的env var。

示例输出

[
  {
    "apiVersion": "v1",
    "kind": "Service",
    "metadata": {
      "annotations": {
        "description": "Exposes and load balances the node.js application pods"
      },
      "name": "myapp-web"
    },
    "spec": {
      "ports": [
        {
          "name": "web",
          "port": 3000,
          "protocol": "TCP",
          "targetPort": 3000
        }
      ],
      "selector": {
        "name": "myapp"
      }
    }
  },
  {
    "apiVersion": "v1",
    "kind": "Route",
    "metadata": {
      "name": "myapp-web"
    },
    "spec": {
      "host": "app.internal.io",
      "port": {
        "targetPort": "web"
      },
      "to": {
        "kind": "Service",
        "name": "myapp-web"
      }
    }
  },
  {
    "apiVersion": "v1",
    "kind": "DeploymentConfig",
    "metadata": {
      "annotations": {
        "description": "Defines how to deploy the application server"
      },
      "name": "myapp"
    },
    "spec": {
      "replicas": 1,
      "selector": {
        "name": "myapp"
      },
      "strategy": {
        "type": "Rolling"
      },
      "template": {
        "metadata": {
          "labels": {
            "name": "myapp"
          },
          "name": "myapp"
        },
        "spec": {
          "containers": [
            {
              "env": [
                {
                  "name": "A_ENV",
                  "value": "a-value"
                },
                {
                  "name": "B_ENV",
                  "value": "b-value"
                }
              ],
              "image": "node",
              "name": "myapp-node",
              "ports": [
                {
                  "containerPort": 3000,
                  "name": "app",
                  "protocol": "TCP"
                }
              ]
            }
          ]
        }
      },
      "triggers": [
        {
          "type": "ConfigChange"
        }
      ]
    }
  }
]

这是可行的,还是与jq太过分了,我应该在python或node中这样做?

编辑1

我刚刚意识到env数组的条件添加/更新已经由|=语法处理了! 所以,我基本上只需要能够将相同的结构作为输入返回,并在相关数组中添加相关的env var。

1 个答案:

答案 0 :(得分:3)

你几乎拥有它,但是你想要重构你的过滤器以保留完整的结果。您希望确保没有任何过滤器更改上下文。从.items开始,您将其从根对象更改为items数组。这本身并不是一个问题,而是你做的事情。请记住,分配/更新会在应用更改时保留原始上下文。因此,如果您根据更新编写过滤器,它将适合您。

为此,您要在env项中找到第一个容器的DeploymentConfig数组。首先让我们找到:

.items[] | select(.kind == "DeploymentConfig").spec.template.spec.containers[0].env

您不需要在错误处理方面做任何其他事情,因为select根本不会产生任何结果。从那里,您只需要通过添加新值来更新数组。

(.items[] | select(.kind == "DeploymentConfig").spec.template.spec.containers[0].env) +=
    [{name:"B_ENV",value:"b-value"}]

如果数组存在,它将添加新项。如果没有,它将创建一个新的env数组。如果env不是数组,那将是一个不同的问题。