首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何用jq解析结构是非线性的json?

如何用jq解析结构是非线性的json?
EN

Stack Overflow用户
提问于 2020-01-20 20:18:55
回答 3查看 232关注 0票数 0

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

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

代码语言:javascript
复制
{
  "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"
  }
}
EN

回答 3

Stack Overflow用户

发布于 2020-01-20 21:35:01

jq 1.6中,您可以使用以下内容:

代码语言:javascript
复制
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输入,并将具有User : "user1"键/值对的对象的password字段设置为您想要的值。

你可以使用try it here

在以前的版本中,您可以使用以下等效项:

代码语言:javascript
复制
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"))'

你可以使用try it here

票数 2
EN

Stack Overflow用户

发布于 2020-01-21 16:09:55

在以下针对所述问题的解决方案中,有两个步骤。第一步使用with_entries选择相关的“配置文件”对象,第二步使用walk更新密码。参数化所有东西都很容易,所以为了简单起见,让我们假设(就像在Q中一样)用户是"user1":

代码语言:javascript
复制
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)并将其复制到您的jq程序的开头。

票数 1
EN

Stack Overflow用户

发布于 2020-01-20 21:58:49

欢迎使用StackOverflow!

完整的示例不是一个有效的JSON对象,也没有试图解决这个问题,也没有一个期望的结果应该是什么样子的示例。因此,下面的答案带有一些猜测。如果将示例修剪成一个去除了所有杂乱的minimal, reproducible example,则可能会大大增加获得好答案的几率。

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

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

您并没有确切地说明DualEndPointLocal对象是什么。

因为文本DualEndPoint只出现在

代码语言:javascript
复制
"Type": "ConnectionProfile:FileTransfer:DualEndPoint"

我假设DualEndPoint对象包含一个键-值对,其格式为

代码语言:javascript
复制
"Type": "...:DualEndPoint"

Local对象是相同的,但是用Local替换了DualEndPoint。如果这种解释是正确的,那么您的第一个代码片段中的两个不同嵌套级别(这就是我所理解的“非线性”部分)中的Local对象将有三个示例。

Local对象的一个示例是:

代码语言:javascript
复制
{
  "Type": "Endpoint:Src:Local",
  "User": "user1",
  "Port": "0",
  "OsType": "Windows",
  "HostName": "Local",
  "Password": "newpassword",
  "HomeDirectory": "/user1homedir"
}

没有任何类似的对象的例子,尽管包含"User“属性,但不应该被更新。因此,为了回答这个问题,这些类型的对象之间的区别也是完全不必要的?

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

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

代码语言:javascript
复制
$ 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,嵌套的对象名称是可变的,因此在查找“

”属性时不能对这些值进行编码。

因此,这听起来像是您想要任意递归查找具有"User“属性的对象。jq的一些递归组合器是..,更通用的recurse,在这个上下文中似乎更合适的是walk

代码语言:javascript
复制
$ jq 'walk(if type == "object" and .User == "user1"
           then .Password = "derp"
           else . end)' full.json

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

请看他的jqplay示例或this jqplay example

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

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

代码语言:javascript
复制
$ jq 'walk(if type == "object" and has("User")
           then (if .User == "user1"
                 then .Password = "derp"
                 else null end)
           else . end)' full.json

这看起来几乎可以工作(请参见this jqplay example),除了它将"foo": null值保留为遍历的结果。这是已经递归到包含"User“属性的对象中的副产品,这使得很难表达应该删除父键-值对。

要解决这个问题,我们需要在walk/1的过滤器中向前看,或者创建一个占位符,然后从父对象的角度再次遍历。后面的两种策略如下所示:

代码语言:javascript
复制
$ 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

这似乎是可行的。参见this jqplay example

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/59823154

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档