如何使用 jq 中的流选项从 JSON 文件中检索键和值

Posted

技术标签:

【中文标题】如何使用 jq 中的流选项从 JSON 文件中检索键和值【英文标题】:How to retrieve key and value from JSON file using stream option in jq 【发布时间】:2019-04-14 19:59:47 【问题描述】:

我有一个结构如下的 json 文件:

 
    "A": [
        "B": 
            "C": [
                "D": 
                    "applicationNumberText": 
                        "value": "15570075",
                        "electronicText": "15570075"
                    ,
                    "date": "2018-10-01",
                    "app": "Utility"
                
            ]
        
    ]

现在我想检索electronicText 值。一种方法是使用如下索引

jq --stream 'select(.[0][1] == "A" and .[0][2] == "B" and .[0][3] == "C") | .[1]'

但在某些情况下结构可能会有所不同,所以我想通过键值而不是索引来引用。我尝试了以下方法,但它不起作用

cat file.json | jq --stream 'select(.A|.[]. B. C|.[]. D.applicationNumberText)'

所以我想在不使用索引的情况下检索 electronicText 键。

Note:我想对大型 JSON 文件使用 stream 选项。

【问题讨论】:

您的第一个 jq 查询使用的键名甚至没有出现在 JSON 中。请修复。此外,问题的描述非常粗略,似乎与标题不同。请关注minimal reproducible example 并指出 .electronicText 的值是否已知为标量。 JSON 不是一个有效的。从 jsonlint.com 修复它 @Inian 感谢您的宝贵回复,我对 JSON 进行了一些更改,请通过此操作。 @peak 我刚刚更正了键名,请仔细阅读。感谢您的宝贵意见。 【参考方案1】:

如果数组的索引是固定的,你可以使用这个过滤器:

jq '.A[0].B.C[0].D.applicationNumberText.electronicText' file

要提取所有electronicText 字段,您可以使用:

jq '.A[].B.C[].D.applicationNumberText.electronicText' file

如果你想使用 jq 流方式(虽然不清楚为什么),你可以使用这个:

jq --stream 'select(.[0]|contains(["electronicText"]))|.[1]//empty'

这将查找字符串electronicText 的键路径,如果找到,则获取其关联值。 //empty是过滤掉没有值的路径数组。

【讨论】:

这仅返回 electronicText key 的值,但我想要 key 和 value 。你能帮我解决这个问题吗?而且我还想更改键名并将此值分配给新键名。例如:electronicText:1890 我想要“appnumber”:1890 我无法获取多键值,例如 value 键和 electronicText 键。你能帮帮我吗? @vikash 我不清楚你到底想要什么。你可能会寻找类似jq --stream '"appnumber": (select(.[0]|contains(["electronicText"]))|.[1]//empty)' file ... @vikash 改变问题的范围不是一个好主意。相反,请创建一个新问题,明确说明您的新问题是什么。 @vikash 您正在更改原始问题的范围。请关闭此问题(通过接受其中一个答案)并提出另一个问题。【参考方案2】:

我相信您正在寻找的是:

. as $inputs |  | setpath($inputs[0]; $inputs[1]) | .A[]?.B.C[]?.D.applicationNumberText.electronicText // empty

此过滤器需要 --stream 作为标志(如您的第一个示例),生成一个带有 json 部分的时间 obj,根据您的第二个示例运行过滤器,然后隐藏空结果。注:This based on a example in the cookbook

让我给你一个概述,以防你感到困惑:

    The --stream flag will make your receive your data differently。现在,您的过滤器将使用 [<path>, <leaf-value>] 形式的数据运行多次,当解析器遍历您的 json 文件时接收到该数据,一次一个节点。

    当您提到使用索引时,我相信您指的是这一点,在这种情况下,这意味着您正在检查 --streaming flag 公开的 path expression value 而不是 实际 json 数据。

    请比较以下输出:

    jq '.A[].B.C[].D.applicationNumberText.electronicText' file.json # outputs 15570075 
    jq --stream '.A[].B.C[].D.applicationNumberText.electronicText' file.json # multiple failures: cannot index array with string "A"
    

    setpath() 接收这些路径和叶值参数将其置于其输入对象之上。例如

     | setpath(["a", 0, "b"], "leaf-value") # returns "a":["b":"leaf-value"]
    

    在我们的例子中,当解析访问每个节点时,我们会生成以下值流:

    "A":["B":"C":["D":"applicationNumberText":"value":"15570075"]]
    "A":["B":"C":["D":"applicationNumberText":"electronicText":"15570075"]]
    "A":["B":"C":["D":"applicationNumberText":"electronicText":null]]
    "A":["B":"C":["D":"date":"2018-10-01"]]
    "A":["B":"C":["D":"app":"Utility"]]
    

    现在数据的格式类似于 json 文件,我们可以运行更熟悉的过滤器。

    .A[]?.B.C[]?.D.applicationNumberText.electronicText
    

    请记住,两者之间没有空格。我们还在生成器.A[].C[] 上使用? 运算符,因为我们不确定这些字段是否始终可以作为数组使用

最后一点,您可能需要考虑使用path() 构建一个路径数组,并将其与使用--stream jq 标志时出现在.[0] 中的路径值进行比较

【讨论】:

你能告诉我使用stream检索valueelectronicText等多键值的确切方向吗? @vikash 我写了一个例子in this gist over in github 并做了一些解释。如果这表明不清楚,请创建另一个关于多值检索的问题【参考方案3】:

回答原来的问题:

jq --stream '
  select(length==2 and .[0][-1]=="electronicText")|.[1]
' input.json
"15570075"

如果您也想要value,那么您可能希望考虑以下过滤器:

select(length==2 and .[0][-2]=="applicationNumberText")
| .[0][-1] as $last
| select($last == "electronicText" or $last == "value")
| ($last): .[1]

使用您的示例 JSON 生成:

"value":"15570075"
"electronicText":"15570075"

将键值对组合成单个 JSON 对象

构造“字典”的一种方法是将inputs 与-n 命令行选项结合使用。只需将上述过滤器包装在结构中即可:

 [inputs | ....] | add

这里 .... 代表上述过滤器;并使用 both -n 和 --stream 选项调用 jq。

【讨论】:

谢谢!这是可行的,但我希望将valueelectronicText 或更多键的值合并到一本字典中,你能帮我吗?例如:"appnumber":1717, "value":1717,"key":corresponding values @vikash - 您要求提供字典,这就是使用 inputs 的查询所产生的结果。如果你想添加更多的键/值对,你必须做一些了解。 SO 不是代码编写服务。【参考方案4】:

如果您知道感兴趣对象的名称,只需按名称搜索对象路径。

getpath(paths(objects) | select(.[-1] == "applicationNumberText"))

流式输入应该有助于解决效率问题。只需确定要保留的路径并截断感兴趣的路径即可。

$ jq -n --stream --arg key 'applicationNumberText' '
fromstream(inputs | truncate_stream2((.[0] | index($key) // empty) + 1))
' input.json

这只是检查路径并查找 "applicationNumberText" 键,然后将路径截断为该值,以便可以从流中重建它。

这使用了 truncate_stream/1 函数的反转版本,它交换了我发现使用更直观的常用输入。

def truncate_stream2($count): .[0] |= .[$count:];

【讨论】:

jq: error: truncate_stream2/1 is not defined at <top-level>, line 2: 我收到了这个错误

以上是关于如何使用 jq 中的流选项从 JSON 文件中检索键和值的主要内容,如果未能解决你的问题,请参考以下文章

jq json根据条件从内部数组中检索值并将其添加到数组之外

使用 JQ 替换 JSON 中的下划线

如何删除 jq 输出中的双引号以在 bash 中解析 json 文件?

如何删除 jq 输出中的双引号以在 bash 中解析 json 文件?

jq-win64.exe:从 Windows 批处理文件中的 JSON 文件解析数据

如何使用 jq 重新格式化 json 中的特定数据