JQ:如何使用嵌入式键更改 JSON 中的值

JQ: How To Change A Value in JSON with embedded keys

我有以下 JSON 从 AWS 机密返回:

{
    "ARN": "MyArn",
    "Name": "MySecret",
    "SecretString": "{\"ConnectionString\":\"MyConnectionString;\",\"SqlCommandTimeout\":\"60\",\",\"ServiceAccountPrincipal\":\"MyServicePrincipal\"}",
    "VersionStages": [
        "AWSCURRENT"
    ],
    "CreatedDate": "2022-01-13T09:08:55.442000-08:00"
}

我希望能够修改 ConnectionString、SqlCommandTimeout 和 ServiceAccountPrincipal 值。为简洁起见,我只展示了对 ConnectionString 的更改。

“新”JSON 看起来像这样:

{
    "ARN": "MyArn",
    "Name": "MySecret",
    "SecretString": "{\"ConnectionString\":\"MyNewConnectionString;\",\"SqlCommandTimeout\":\"60\",\",\"ServiceAccountPrincipal\":\"MyServicePrincipal\"}",
    "VersionStages": [
        "AWSCURRENT"
    ],
    "CreatedDate": "2022-01-13T09:08:55.442000-08:00"
}

但是,我不确定如何使用 JQ 来实现。

我想做的是让 JQ 修改每个字段的值,并使用更新后的值输出 JSON 对象。

我遇到的问题是修改嵌入 JSON 对象内部的这些值。我希望这样的东西能起作用,但它不起作用:

cat json.txt | jq 'SecretString.ConnectionString = "MyNewConnectionString"'

我收到这个错误:

jq: error (at <stdin>:8): Cannot index string with string "ConnectionString"
exit status 5

我做错了什么?

谢谢

SecretString 是对象中的字段名称。因此,您必须在其名称前使用点来解决它:.SecretString

此外,该字段的内容似乎是 JSON,但被编码为无法直接寻址的字符串。您必须首先使用 fromjson 对其进行解码,然后使用 tojson 再次对其进行编码。但是,示例数据中的 JSON 字符串有一个错误,使其无效 JSON(在值 \"60\", 之后有一个引号和一个逗号:\",) .假设这只是一个 copy/paste 错误,让我们继续:

要使用值本身更新值,请使用更新运算符 |=。要仅分配一个值,请使用简单的赋值运算符 =.

最后,你可以把要处理的文件作为参数给jq。您不必先 cat 然后将其通过管道传输到 jq。

总而言之,这就是您要查找的内容:

jq '.SecretString |= (fromjson | .ConnectionString = "MyNewConnectionString" | tojson)' json.txt
{
  "ARN": "MyArn",
  "Name": "MySecret",
  "SecretString": "{\"ConnectionString\":\"MyNewConnectionString\",\"SqlCommandTimeout\":\"60\",\"ServiceAccountPrincipal\":\"MyServicePrincipal\"}",
  "VersionStages": [
    "AWSCURRENT"
  ],
  "CreatedDate": "2022-01-13T09:08:55.442000-08:00"
}

Demo

一些补充说明:

如果您想将内部 JSON 保留为 JSON(以便将来更轻松地访问它),只需使用 tojson)[=25 放弃重新转换=]

要操作多个值,只需将它们添加到第一个值之后:.ConnectionString = "…" | .SqlCommandTimeout = "…" | .ServiceAccountPrincipal = "…"

您还可以将新值作为参数提供,然后将它们作为变量访问,这样可以让您的过滤器变得不那么混乱,尤其是当您打算更改的不仅仅是那个值时领域。

jq --arg v "MyNewConnectionString" '… .ConnectionString = $v …' json.txt

首先,请注意您的示例 SecretString 无效 JSON(有一个虚假的 ,"):

$ echo $ echo '{"ConnectionString":"MyConnectionString;","SqlCommandTimeout":"60",","ServiceAccountPrincipal":"MyServicePrincipal"}' | jq
parse error: Invalid numeric literal at line 1, column 94

如果我们解决了这个问题,我们就可以解决您的问题,即处理嵌套 JSON。 jq 为此目的提供了一个 fromjson function(连同我们稍后会用到的 tojson),所以我们可以得到:

$ cat json.txt | jq '.SecretString | fromjson'
{
  "ConnectionString": "MyConnectionString;",
  "SqlCommandTimeout": "60",
  "ServiceAccountPrincipal": "MyServicePrincipal"
}

我们可以在结果中设置字段 JSON:

$ cat json.txt | jq '.SecretString | fromjson | .ConnectionString = "MyNewConnectionString"'
{
  "ConnectionString": "MyNewConnectionString",
  "SqlCommandTimeout": "60",
  "ServiceAccountPrincipal": "MyServicePrincipal"
}

我不知道是否可以将此赋值应用到原始输入内联(可能是!)但是只需将此输出捕获到变量中然后将其写回原始字符串就足够容易了:

# notice the tojson call at the end to escape the JSON as a string
$ new_secret=$(cat json.txt | jq '.SecretString | fromjson | .ConnectionString = "MyNewConnectionString" | tojson')

$ cat json.txt | jq ".SecretString = ${new_secret}"
{
  "ARN": "MyArn",
  "Name": "MySecret",
  "SecretString": "{\"ConnectionString\":\"MyNewConnectionString\",\"SqlCommandTimeout\":\"60\",\"ServiceAccountPrincipal\":\"MyServicePrincipal\"}",
  "VersionStages": [
    "AWSCURRENT"
  ],
  "CreatedDate": "2022-01-13T09:08:55.442000-08:00"
}

编辑:

啊,pmf 的回答指出了 |= 运算符,它完全满足您的需要:) 我会留下这个答案,但 pmf 的方法更好。

如果问题确实是上游问题,那么除了修复它之外,您还可以在 jq 中编辑格式错误的字符串:

.SecretString
| sub( "MyConnectionString;\""; "MyConnectionString\"")
| sub( "60\",\""; "60\"")

酌情使用 |=fromjsontojson,如本页其他地方所述。

我们必须在 SecretString 的值上使用 fromjsontojson

过滤器

(.SecretString | fromjson | 
.ConnectionString |= "MyNewConnectionString" | tojson) as $secret 
| .SecretString |= $secret

输入

{
    "ARN": "MyArn",
    "Name": "MySecret",
    "SecretString": "{\"ConnectionString\":\"MyConnectionString\",\"SqlCommandTimeout\":\"60\",\"ServiceAccountPrincipal\":\"MyServicePrincipal\"}",
    "VersionStages": [
        "AWSCURRENT"
    ],
    "CreatedDate": "2022-01-13T09:08:55.442000-08:00"
}

输出

{
  "ARN": "MyArn",
  "Name": "MySecret",
  "SecretString": "{\"ConnectionString\":\"MyNewConnectionString\",\"SqlCommandTimeout\":\"60\",\"ServiceAccountPrincipal\":\"MyServicePrincipal\"}",
  "VersionStages": [
    "AWSCURRENT"
  ],
  "CreatedDate": "2022-01-13T09:08:55.442000-08:00"
}

您可以看到 ConnectionString 更新为新值 MyNewConnectionString 作为 json string.

演示

https://jqplay.org/s/HCsGdg1RLU