首页 > 解决方案 > jq过滤器根据数组值忽略select语句中的值

问题描述

给定以下 JSON 输入:

{
  "hostname": "server1.domain.name\nserver2.domain.name\n*.gtld.net",
  "protocol": "TCP",
  "port": "8080\n8443\n9500-9510",
  "component": "Component1",
  "hostingLocation": "DC1"
}

我想获得以下 JSON 输出:

{
  "hostname": [
    "server1.domain.name",
    "server2.domain.name",
    "*.gtld.net"
  ],
  "protocol": "TCP",
  "port": [
    "8080-8080",
    "8443-8443",
    "9500-9510"
  ],
  "component": "Component1",
  "hostingLocation": "DC1"
}

考虑:

  1. 数组中的各个值port可能会或可能不会被-字符分隔(我无法控制)。
  2. 如果数组中的单个值port不包含-分隔符,那么我需要添加它,然后在-分隔符之后重复数组值。例如,8080变成8080-80808443变成8443-8443等等。
  3. 最后,如果port数组中的值已经是 format value-value,我应该保持不变。

jq在阅读了这里和官方在线文档中的许多示例之后,我整个下午都在对这个过滤器猛烈抨击。我根本不知道如何适应上面的考虑#3。

我现在拥有的过滤器:

{hostname: .hostname | split("\n"), protocol: .protocol, port: .port | split("\n") | map(select(. | contains("-") | not)+"-"+.), component: .component, hostingLocation: .hostingLocation}

产生以下输出 JSON :

{
  "hostname": [
    "server1.domain.name",
    "server2.domain.name",
    "*.gtld.net"
  ],
  "protocol": "TCP",
  "port": [
    "8080-8080",
    "8443-8443"
  ],
  "component": "Component1",
  "hostingLocation": "DC1"
}

正如您在上面看到的,我随后丢失了该9500-9510值,因为它已经包含-我的过滤器清除的字符串。

如果我的逻辑没有让我失望,我需要if在我的select语句中添加一条语句,以有条件地仅将不包含字符串的数组值发送-到我的 select 语句,但保留包含分隔符的数组值不变。但是,我似乎无法弄清楚最后一块。

我很乐意接受任何产生所需输出的替代过滤器,但是我也非常渴望了解我的逻辑在上述过滤器中失败的地方。

提前感谢任何花费宝贵时间帮助我的人!

/乔尔

标签: jsonjq

解决方案


首先,我们用换行符 ( .hostname /= "\n") 分割主机名字符串,并对端口字符串 ( .port /= "\n") 执行相同操作。实际上,我们可以将这些相同的操作合二为一:(.hostname, .port) /= "\n"

接下来,对于端口数组.port[](第二个元素,如果存在,否则再次出现第一个元素 ( )split("[^\\d]";"g").[0].[1]//.[0]

在名为 input.json 的文件中输入内容后,以下内容应将其转换为所需的格式:

jq '

  (.hostname, .port) /= "\n" |
  .port[] |= (split("[^\\d]";"g") | "\(.[0])-\(.[1]//.[0])")

' input.json

关于您的考虑:

  1. 当我们在任何非数字字符处拆分时,其他字符分隔端口范围的值没有区别。如果多个字符可以将它们分开(例如箭头->或在破折号前后有空格-),只需将正则表达式替换[^\\d][^\\d]+以捕获多个非数字字符。
  2. 和 3. 我们总是通过包含一个破折号和第二个值来生成一个范围,这取决于第二个项目的存在,可能是那个,也可能是第一个。

关于你的方法:

map您使用的内部,select它评估是否不满足empty条件( )。contains("-") | not由于"9500-9510"确实包含一个破折号,它没有幸存下来。if语句的语句无济于事,select因为即使select不评估empty它仍然不会修改任何内容,它只是复制其输入不变。因此,如果select允许通过两种情况(包含和不包含破折号)它变得无用。但是,您可以使用if语句之外select语句,但我认为上述解决方案是一种更简单的方法。


推荐阅读