【问题标题】:JQ: How To Change A Value in JSON with embedded keysJQ:如何使用嵌入的键更改 JSON 中的值
【发布时间】:2022-05-05 13:46:29
【问题描述】:

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

{
    "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 对象内的这些值。我希望这样的事情能够奏效,但它没有:

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

我收到此错误:

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

我做错了什么?

谢谢

【问题讨论】:

  • \"SqlCommandTimeout\":\"60\" 后面有一个无效的\" 字符,导致该字段无法转换回 JSON
  • 这是出乎意料吗?
  • 不幸的是,AWS 正在添加这些字符。

标签: json jq aws-secrets-manager


【解决方案1】:

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

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

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

最后,您可以将要处理的文件作为参数提供给 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 放弃重新转换)

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

您还可以将新值作为参数提供,并将它们作为变量访问,这样可以使您的过滤器不那么混乱,特别是如果您打算更改的不仅仅是一个字段。

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

【讨论】:

    【解决方案2】:

    首先,请注意您的示例 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 的方法更好。

    【讨论】:

    • 感谢大家的帮助。 JSON 中“虚假”字符的问题是 AWS CLI 返回的内容。这就是为什么我之前的 JQ 尝试失败的症结所在(正如这里的几个人所指出的那样)。
    【解决方案3】:

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

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

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

    【讨论】:

      【解决方案4】:

      我们必须对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 字符串

      演示

      https://jqplay.org/s/HCsGdg1RLU

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-12-15
        • 2017-07-31
        • 1970-01-01
        • 2018-04-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多