jq-如何遍历具有不同名称的嵌套键?

时间:2020-08-21 03:26:00

标签: json jq

我有以下格式的数据。键在所有元素之间都是唯一的。

{
  "backend1": {
    "product1": {
      "subservice1": {
        "env1": {
          "KMS": "0.21",
          "DynamoDB": "235.91",
          "ElastiCache": "108.85",
          "Elastic Load Balancing": "324.29"
        },
        "env2": {
          "KMS": "0.21",
          "Elastic Load Balancing": "3.78"
        }
      }
    }
  },
  "backend2": {
    "product2": {
      "subservice2": {
        "env1": {
          "KMS": "0.21",
          "ElastiCache": "108.85",
          "Elastic Load Balancing": "41.18"
        },
        "env2": {
          "KMS": "0.21",
          "Elastic Load Balancing": "3.78"
        }
      }
    }
  }
}

我想遍历键并将JSON转换为Prometheus度量格式。

aws_cost{env="env1",aws_service="KMS",product="product1",backend="backend1",subservice="subservice1"} 0.21
aws_cost{env="env1",aws_service="DynamoDB",product="product1",backend="backend1",subservice="subservice1"} 235.91
aws_cost{env="env1",aws_service="ElastiCache",product="product1",backend="backend1",subservice="subservice1"} 108.85
.....

我发现可以使用.[] | .[] | .[] | .[]来获得不同索引处的键,但是我不知道如何使用嵌套的for循环来生成上述数据。我愿意接受其他解决方案。

2 个答案:

答案 0 :(得分:3)

由于对象的键确定了不同的值,因此,如果将这些值传入流中,则可以做得很好。然后,只需解析路径的各个部分并构建结果即可。

流,解析然后输出。

$ jq --stream -r '
select(length == 2) as [[$backend, $product, $subservice, $env, $aws_service], $value]
  | {$env, $aws_service, $product, $backend, $subservice}
  | "aws_cost{\([to_entries[] | "\(.key)=\(.value|tojson)"] | join(","))} \($value)"
' input.json
aws_cost{env="env1",aws_service="KMS",product="product1",backend="backend1",subservice="subservice1"} 0.21
aws_cost{env="env1",aws_service="DynamoDB",product="product1",backend="backend1",subservice="subservice1"} 235.91
aws_cost{env="env1",aws_service="ElastiCache",product="product1",backend="backend1",subservice="subservice1"} 108.85
aws_cost{env="env1",aws_service="Elastic Load Balancing",product="product1",backend="backend1",subservice="subservice1"} 324.29
aws_cost{env="env2",aws_service="KMS",product="product1",backend="backend1",subservice="subservice1"} 0.21
aws_cost{env="env2",aws_service="Elastic Load Balancing",product="product1",backend="backend1",subservice="subservice1"} 3.78
aws_cost{env="env1",aws_service="KMS",product="product2",backend="backend2",subservice="subservice2"} 0.21
aws_cost{env="env1",aws_service="ElastiCache",product="product2",backend="backend2",subservice="subservice2"} 108.85
aws_cost{env="env1",aws_service="Elastic Load Balancing",product="product2",backend="backend2",subservice="subservice2"} 41.18
aws_cost{env="env2",aws_service="KMS",product="product2",backend="backend2",subservice="subservice2"} 0.21
aws_cost{env="env2",aws_service="Elastic Load Balancing",product="product2",backend="backend2",subservice="subservice2"} 3.78

答案 1 :(得分:1)

我想出了两种方法,一种有点深奥,另一种有点蛮力。我同时提供了两者,以便您可以选择自己喜欢的(并尝试同时理解两者)。

神秘:

. as $data |
paths(scalars) | . as $path |
"aws_cost{env=\"\($path[3])\",aws_service=\"\($path[4])\",product=\"\($path[1])\",backend=\"\($path[0])\",subservice=\"\($path[2])\"} \($data | getpath($path))"

paths(scalars)生成到输入中所有叶节点(不是对象或数组)的路径。每个路径都是一个像["backend1","product1","subservice1","env1","KMS"]这样的数组。您可以看到我们的处理方法–使用路径本身来格式化标签列表,并使用getpath来获取实际值。使用了一些as改组,以便getpath有权操作.

蛮力:

to_entries[] | .key as $backend | .value | 
to_entries[] | .key as $product | .value |
to_entries[] | .key as $subservice | .value |
to_entries[] | .key as $env | .value |
to_entries[] | .key as $service | 
"aws_cost{env=\"\($env)\",aws_service\"\($service)\",product=\"\($product)\",backend=\"\($backend)\",subservice=\"\($subservice)\"} \(.value)"

to_entries将对象转换成key / value对的数组。例如,{"a":1,"b":2}变为[{"key":"a","value:1},{"key":"b","value":2}]。我们可以使用它来遍历每个级别的键,使用as捕获键以供以后使用,然后将值放在.中以进行进一步迭代。然后,当我们到达最底层时,它只是字符串格式。

任何一种都应与jq -r一起运行,以使输出字符串不会被重新编码为JSON。