0

我希望我已经清楚地表达了我的问题。需要帮助查询,他们使用 JQ 解析多个 json 文件,其中每个文件中的结构是非线性的。该应用程序生成类似于此示例的配置数据。每个文件可以有零个或多个 DualEndPoint 或 Local 对象。我需要能够在“用户”属性中查询特定用户并插入新密码以重新提交回 api。对于 DualEndPoints,嵌套对象名称是可变的,因此无法在查找“用户”属性时对这些值进行编码。

如果找到特定用户的匹配项,则返回仅插入该用户的新密码的整个结构。在示例中,查询 user1 将返回整个 PROFILE1 和 PROFILE2,但不返回 PROFILE3,因为它不包含 user1 凭据。

{
  "PROFILE1": {
    "Type": "ConnectionProfile:FileTransfer:DualEndPoint",
    "WorkloadAutomationUsers": [
      "*"
    ],
    "VerifyBytes": true,
    "TargetAgent": "sqlrptvmjhbpr01",
    "TargetCTM": "Production",
    "Endpoint:Src:Local_0": {
      "Type": "Endpoint:Src:Local",
      "User": "user1",
      "Port": "0",
      "OsType": "Windows",
      "HostName": "Local",
      "Password": "*****",
      "HomeDirectory": "/user1homedir"
    },
    "Endpoint:Dest:SFTP_1": {
      "Type": "Endpoint:Dest:SFTP",
      "User": "user2",
      "HostName": "server2",
      "Password": "*****",
      "HomeDirectory": "/user2homedir"
    }
  },
  "PROFILE2": {
    "Type": "ConnectionProfile:FileTransfer:Local",
    "WorkloadAutomationUsers": [
      "*"
    ],
    "VerifyBytes": true,
    "User": "user1",
    "VerifyDestination": true,
    "OsType": "Windows",
    "HostName": "Local",
    "Password": "*****",
    "TargetAgent": "server1",
    "TargetCTM": "Production"
  },
  "PROFILE3": {
    "Type": "ConnectionProfile:FileTransfer:Local",
    "WorkloadAutomationUsers": [
      "*"
    ],
    "VerifyBytes": true,
    "User": "user3",
    "OsType": "Windows",
    "HostName": "Local",
    "Password": "*****",
    "HomeDirectory": "/user3hoemdir",
    "TargetAgent": "server2",
    "TargetCTM": "Production"
  }
}
4

3 回答 3

2

使用jq1.6,您可以使用以下内容:

jq --arg newPwd "newPassword" \
     'walk(if type == "object" and .User == "user1" then .password |= $newPwd else . end)  
        | map_values(select(.. | select(type == "object") and .User == "user1"))' 

这将递归您的 JSON 输入并将password具有User : "user1"键/值对的对象字段设置为您想要的值。

你可以在这里试试

在以前的版本中,您可以使用这个等价物:

jq --arg newPwd "newPassword" \
   'def rec :
      if type == "object" and .User == "user1" then 
        .password = $newPwd
      elif type == "object" then
        map_values(rec)
      elif type == "array" then
        map(rec)
      else
        .
      end
    ; 
    rec  | map_values(select(.. | select(type == "object") and .User == "user1"))'

你可以在这里试试

于 2020-01-20T13:35:00.887 回答
1

在以下对所述问题的解决方案中,有两个步骤。第一步用于with_entries选择相关的“PROFILE”对象,第二步用于walk在有密码的情况下更新密码。参数化一切都很容易,所以为了简单起见,我们假设(如在 Q 中)用户是“user1”:

with_entries(select( .value
    | any(paths(. == "user1");
          .[-1] == "User" )))
| walk( if type == "object" and .User == "user1" and has("Password")
        then .Password = "newpassword"
        else .end)

这里的使用any会使事情复杂化,但为了提高效率。

注意事项walk/1

如果你的 jq 没有walk/1,那么现在是更新你的 jq 的好时机,但如果这不是一个选项,只需谷歌搜索它的 def(搜索词:jq def walk builtin.jq)并将 def 复制到开头你的 jq 程序。

于 2020-01-21T08:09:55.367 回答
0

欢迎来到 StackOverflow!

完整的示例不是一个有效的 JSON 对象,并且没有尝试解决问题,或者期望结果应该是什么样子的示例。因此,以下答案带有一些猜测。如果将示例缩减为一个最小的、可重现的示例,其中所有的混乱都被删除,它可能会大大增加获得一个好的答案的几率。

例如,"TargetAgent": "sqlrptvmjhbpr01"似乎是不相关的信息。这条线的唯一作用是增加读者的认知负担,试图破译它是否与问题任务相关,而事实并非如此。

每个文件可以有零个或多个 DualEndPoint 或 Local 对象。

您没有确切地说出 a DualEndPointor Localobject 是什么。

因为文本DualEndPoint只出现在上下文中

"Type": "ConnectionProfile:FileTransfer:DualEndPoint"

我假设一个DualEndPoint对象是一个包含具有格式的键值对的对象

"Type": "...:DualEndPoint"

并且一个Local对象是相同的,但DualEndPoint被替换为Local。如果该解释是正确的,那么Local您的第一个代码片段中将有三个对象示例,位于两个不同的嵌套级别(我将其理解为“非线性”部分)。

对象的一个​​示例Local是:

{
  "Type": "Endpoint:Src:Local",
  "User": "user1",
  "Port": "0",
  "OsType": "Windows",
  "HostName": "Local",
  "Password": "newpassword",
  "HomeDirectory": "/user1homedir"
}

尽管包含“用户”属性,但没有任何类似对象的示例不应更新。那么看来,回答这个问题,这些类型的对象之间的区别也是完全没有必要的吗?

我需要能够在“用户”属性中查询特定用户并插入新密码以重新提交回 api。

因此,您的主要问题的一个子问题可能是使用新密码更新对象(如果它是正确的User)。假设您已将对象范围缩小到这样的对象,程序的子部分可能如下所示:

$ jq 'if .User == "user1" then .Password = "derp" else . end' local1.json
{
  "Type": "Endpoint:Src:Local",
  "User": "user1",
  "Port": "0",
  "OsType": "Windows",
  "HostName": "Local",
  "Password": "derp",
  "HomeDirectory": "/user1homedir"
}

对于 DualEndPoints,嵌套对象名称是可变的,因此无法在查找“用户”属性时对这些值进行编码。

所以听起来你想任意递归寻找具有“用户”属性的对象。一些 jq 的递归组合器是..,在这种情况下更通用recurse并且似乎更合适,walk

$ jq 'walk(if type == "object" and .User == "user1"
           then .Password = "derp"
           else . end)' full.json

(这也是 Aaron 发布的内容,除了他使用|=和我使用=。)

请参阅他的 jqplay 示例或此 jqplay 示例

如果找到特定用户的匹配项,则返回仅插入该用户的新密码的整个结构。在示例中,查询 user1 将返回整个 PROFILE1 和 PROFILE2,但不返回 PROFILE3,因为它不包含 user1 凭据。

这听起来像是我们使用的表达式中的一个额外条件:

$ jq 'walk(if type == "object" and has("User")
           then (if .User == "user1"
                 then .Password = "derp"
                 else null end)
           else . end)' full.json

这似乎几乎可以工作(参见这个 jqplay 示例),除了它留下"foo": null的值是行走的结果。这是已经递归到包含“用户”属性的对象的副产品,这使得很难表达应该删除父键值对。

walk/1解决这个问题,我们需要在' 过滤器中向前看,或者创建一个占位符并以父对象的视角再次行走。此处演示了这两种策略中的后者:

$ jq 'walk(if type == "object" and has("User")
      then (if .User == "user1" then .Password = "derp" else "wat" end)
      else . end)
      | walk(if type == "object"
             then with_entries(select(.value != "wat"))
             else . end)' full.json

这似乎有效。请参阅此 jqplay 示例

于 2020-01-20T13:58:48.730 回答