在迭代值之前,如何检查jq中是否存在'key'的存在

107
我从下面的查询中得到了"Cannot iterate over null (null)"的错误,因为"result"对象中没有".property_history"属性。
在使用"map(...)"之前,我该如何检查".property_history"属性是否存在?
我尝试使用了类似的方法。
sold_year= `echo "$content" | jq 'if has("property_history") then 
map(select(.event_name == "Sold"))[0].date' else null end

原始查询:

sold_year=`echo "$content" | jq '.result.property_history | map(select(.event_name == "Sold"))[0].date'`

JSON:

{
    "result":{
        "property_history":[
            {
                "date":"01/27/2016",
                "price_changed":0,
                "price":899750,
                "event_name":"Listed",
                "sqft":0
            },
            {
                "date":"12/15/2015",
                "price_changed":0,
                "price":899750,
                "event_name":"Listed",
                "sqft":2357
            },
            {
                "date":"08/30/2004",
                "price_changed":0,
                "price":739000,
                "event_name":"Sold",
                "sqft":2357
            }
        ]
    }
}
6个回答

110
你可以在 jq 中使用选择表达式来实现你想要的目标,例如:
jq '.result 
  | select(.property_history != null) 
  | .property_history 
  | map(select(.event_name == "Sold"))[0].date'

21
将"select(.property_history != null)"简化为"select(.property_history)"即可,两者表达的意思相同,但后者更加简洁明了。 - icenac
6
@icenac - 这两个表达并不等价。注意! - peak
@peak 我知道,但对于这个特定的例子,它们是这样的。 - icenac
特殊情况并不特殊到足以打破规则。 - undefined

40
从技术上讲,为了测试属性的存在,你应该使用has/1,但在当前环境下,最好使用后缀?运算符,例如:
$ jq '.result 
  | .property_history[]?
  | select(.event_name == "Sold") 
  | .date'
"08/30/2004"

如果存在 .result 的值不是 JSON 对象的可能性,那么你可以将上面的第二行替换为:
try(.property_history[])

2
很酷。我尝试过了...但它返回的是“布尔值”。看起来诀窍在于“.[]”。 - Rahul Dess
3
您可以将其简化为 jq '.result.property_history[]? | select(.event_name == "Sold") | .date' - Olaf Dietsche
3
这比被接受的答案更加简洁。 - z2s8
property_history 不存在时,此答案会导致错误。Olaf 在注释中提供的缩写版本可以正常工作。 - aff

32
使用has("mykey1")(用于对象)或has(0)(用于数组):
jq 'has("name")' <<< '{"name": "hello"}'

输出:

true

7
如果 [ "$( jq 'has("alert")' <<< $this_alert_json )" == "true" ]; 则 - 123
2
整洁。清晰简明。+1 - Neeraj
2
针对 POSIX shell(不包含重定向功能):echo "{\"name\": \"hello\"}" | jq 'has("name")' - 123
如果您不确定传入的字符串是否为有效的JSON:echo "{\"name\": \"hello\"}" | jq -R 'fromjson? | has("name")',这将不会因为错误的JSON而抛出错误,如果这是您想要的。 - 123
1
我可以在这里提供JSON文件吗? - Srinivasu
@Srinivasu https://dev59.com/cpXfa4cB1Zd3GeqPgIIb#36273303 - 123

6

一般模式:

try (...) // "default_value"

根据你的逻辑:

jq 'try (.result.property_history | map(select(.event_name == "Sold"))[0].date) // "default_value"'

try (没有catch) 如果表达式失败,则返回空。 // 如果该值为空,则提供默认值。


非常感谢你的分享!我之前还没有看到try功能,我觉得这是最简洁优雅的解决方案。 - undefined

5

诀窍是结合 //empty 使用:

jq '.result.property_history // empty | map(select(.event_name == "Sold"))[0:1][].date'

另一种选择是使用额外的下拉列表(select):

jq '.result.property_history | select(.) | map(select(.event_name == "Sold"))[0:1][].date'

5

最短的代码(并且有额外的奖励,即设置退出代码):

# Example that exits with code 0
jq -e 'has("result")'

# Example that exists with code 0
jq -e '.result[].property_history | has("price")'

# Example that exists with code 1
jq -e '.result[].property_history | has("dummyKey")'

在这些示例中,由于设置了标志-e,如果has返回true,则退出代码将被设置为0,否则为1。这样可以节省不必要的额外步骤。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接