【问题标题】:Can jq handle nested JSON data?jq 可以处理嵌套的 JSON 数据吗?
【发布时间】:2016-05-04 07:05:14
【问题描述】:

也许这对于“jq”来说太过分了,我需要跳入 python json 模块,但是让我们为大师们提出一个挑战!!

我有一些看起来有点像这样(简化)的 JSON 数据(来自 i3 窗口管理器)。我想深入挖掘任意数量的 .nodes/.floating_nodes 级别并拉出“window”设置为非空的 .nodes 或 .floating_nodes。 :

{
  "floating_nodes": [],
  "nodes": [
    {
      "floating_nodes": [],
      "nodes": [
        {
          "floating_nodes": [],
          "nodes": [
            {
              "floating_nodes": [],
              "nodes": [],
              "window": null,
              "name": "__i3_scratch",
            }
          ],
          "window": null,
          "name": "foobar",
        }
      ],
      "window": null,
      "name": "__i3",
    },
    {
      "floating_nodes": [],
      "nodes": [
        {
          "floating_nodes": [],
          "nodes": [],
          "window": null,
          "name": "topdock",
        },
        {
          "floating_nodes": [],
          "nodes": [
            {
              "floating_nodes": [],
              "nodes": [
                {
                  "floating_nodes": [],
                  "nodes": [],
                  "window": 8388613,
                  "name": "16:11 bhepple:.../~ — Konsole",
                  "rect": {
                    "height": 1061,
                    "width": 1920,
                    "y": 0,
                    "x": 0
                  },
              ],
              "window": null,
              "name": "1",
            }
          ],
          "window": null,
          "name": "content",
        },
        {
          "floating_nodes": [],
          "nodes": [
            {
              "floating_nodes": [],
              "nodes": [],
              "window": 14680070,
              "name": "i3bar for output VNC-0",
            }
          ],
          "window": null,
          "name": "bottomdock",
        }
      ],
      "window": null,
      "name": "VNC-0",
    }
  ],
  "window": null,
  "name": "root",
}

我的简化示例有点破坏了我的 JSON 示例,所以这里是完整版本:

{"id":10883136,"type":"root","orientation":"horizontal","scratchpad_state":"none","percent":null,"urgent":false,"focused":false,"layout":"splith","workspace_layout":"default","last_split_layout":"splith","border":"pixel","current_border_width":-1,"rect":{"x":0,"y":0,"width":1920,"height":1080},"deco_rect":{"x":0,"y":0,"width":0,"height":0},"window_rect":{"x":0,"y":0,"width":0,"height":0},"geometry":{"x":0,"y":0,"width":0,"height":0},"name":"root","window":null,"nodes":[{"id":10883568,"type":"output","orientation":"none","scratchpad_state":"none","percent":0.5,"urgent":false,"focused":false,"layout":"output","workspace_layout":"default","last_split_layout":"splith","border":"pixel","current_border_width":-1,"rect":{"x":0,"y":0,"width":1920,"height":1080},"deco_rect":{"x":0,"y":0,"width":0,"height":0},"window_rect":{"x":0,"y":0,"width":0,"height":0},"geometry":{"x":0,"y":0,"width":0,"height":0},"name":"__i3","window":null,"nodes":[{"id":10884128,"type":"con","orientation":"horizontal","scratchpad_state":"none","percent":null,"urgent":false,"focused":false,"layout":"splith","workspace_layout":"default","last_split_layout":"splith","border":"pixel","current_border_width":-1,"rect":{"x":0,"y":0,"width":0,"height":0},"deco_rect":{"x":0,"y":0,"width":0,"height":0},"window_rect":{"x":0,"y":0,"width":0,"height":0},"geometry":{"x":0,"y":0,"width":0,"height":0},"name":"content","window":null,"nodes":[{"id":10884720,"type":"workspace","orientation":"none","scratchpad_state":"none","percent":null,"urgent":false,"focused":false,"layout":"splith","workspace_layout":"default","last_split_layout":"splith","border":"pixel","current_border_width":-1,"rect":{"x":0,"y":0,"width":0,"height":0},"deco_rect":{"x":0,"y":0,"width":0,"height":0},"window_rect":{"x":0,"y":0,"width":0,"height":0},"geometry":{"x":0,"y":0,"width":0,"height":0},"name":"__i3_scratch","num":-1,"window":null,"nodes":[],"floating_nodes":[],"focus":[],"fullscreen_mode":1,"sticky":false,"floating":"auto_off","swallows":[]}],"floating_nodes":[],"focus":[10884720],"fullscreen_mode":0,"sticky":false,"floating":"auto_off","swallows":[]}],"floating_nodes":[],"focus":[10884128],"fullscreen_mode":0,"sticky":false,"floating":"auto_off","swallows":[]},{"id":10886256,"type":"output","orientation":"none","scratchpad_state":"none","percent":0.5,"urgent":false,"focused":false,"layout":"output","workspace_layout":"default","last_split_layout":"splith","border":"pixel","current_border_width":-1,"rect":{"x":0,"y":0,"width":1920,"height":1080},"deco_rect":{"x":0,"y":0,"width":0,"height":0},"window_rect":{"x":0,"y":0,"width":0,"height":0},"geometry":{"x":0,"y":0,"width":0,"height":0},"name":"VNC-0","window":null,"nodes":[{"id":10886944,"type":"dockarea","orientation":"none","scratchpad_state":"none","percent":null,"urgent":false,"focused":false,"layout":"dockarea","workspace_layout":"default","last_split_layout":"splith","border":"pixel","current_border_width":-1,"rect":{"x":0,"y":0,"width":1920,"height":0},"deco_rect":{"x":0,"y":0,"width":0,"height":0},"window_rect":{"x":0,"y":0,"width":0,"height":0},"geometry":{"x":0,"y":0,"width":0,"height":0},"name":"topdock","window":null,"nodes":[],"floating_nodes":[],"focus":[],"fullscreen_mode":0,"sticky":false,"floating":"auto_off","swallows":[{"dock":2,"insert_where":2}]},{"id":10887648,"type":"con","orientation":"horizontal","scratchpad_state":"none","percent":null,"urgent":false,"focused":false,"layout":"splith","workspace_layout":"default","last_split_layout":"splith","border":"pixel","current_border_width":-1,"rect":{"x":0,"y":0,"width":1920,"height":1061},"deco_rect":{"x":0,"y":0,"width":0,"height":0},"window_rect":{"x":0,"y":0,"width":0,"height":0},"geometry":{"x":0,"y":0,"width":0,"height":0},"name":"content","window":null,"nodes":[{"id":10889056,"type":"workspace","orientation":"horizontal","scratchpad_state":"none","percent":null,"urgent":false,"focused":false,"layout":"splith","workspace_layout":"default","last_split_layout":"splith","border":"pixel","current_border_width":-1,"rect":{"x":0,"y":0,"width":1920,"height":1061},"deco_rect":{"x":0,"y":0,"width":0,"height":0},"window_rect":{"x":0,"y":0,"width":0,"height":0},"geometry":{"x":0,"y":0,"width":0,"height":0},"name":"1","num":1,"window":null,"nodes":[{"id":10895664,"type":"con","orientation":"none","scratchpad_state":"none","percent":1.0,"urgent":false,"focused":true,"layout":"splith","workspace_layout":"default","last_split_layout":"splith","border":"pixel","current_border_width":1,"rect":{"x":0,"y":0,"width":1920,"height":1061},"deco_rect":{"x":0,"y":0,"width":0,"height":0},"window_rect":{"x":1,"y":1,"width":1918,"height":1059},"geometry":{"x":0,"y":0,"width":958,"height":1059},"name":"16:11 bhepple:.../~ — Konsole","window":8388613,"window_properties":{"class":"konsole","instance":"konsole","title":"16:11 bhepple:.../~ — Konsole","transient_for":null},"nodes":[],"floating_nodes":[],"focus":[],"fullscreen_mode":0,"sticky":false,"floating":"auto_off","swallows":[]}],"floating_nodes":[],"focus":[10895664],"fullscreen_mode":1,"sticky":false,"floating":"auto_off","swallows":[]}],"floating_nodes":[],"focus":[10889056],"fullscreen_mode":0,"sticky":false,"floating":"auto_off","swallows":[]},{"id":10888352,"type":"dockarea","orientation":"none","scratchpad_state":"none","percent":null,"urgent":false,"focused":false,"layout":"dockarea","workspace_layout":"default","last_split_layout":"splith","border":"pixel","current_border_width":-1,"rect":{"x":0,"y":1061,"width":1920,"height":19},"deco_rect":{"x":0,"y":0,"width":0,"height":0},"window_rect":{"x":0,"y":0,"width":0,"height":0},"geometry":{"x":0,"y":0,"width":0,"height":0},"name":"bottomdock","window":null,"nodes":[{"id":10891392,"type":"con","orientation":"none","scratchpad_state":"none","percent":1.0,"urgent":false,"focused":false,"layout":"splith","workspace_layout":"default","last_split_layout":"splith","border":"pixel","current_border_width":1,"rect":{"x":0,"y":1061,"width":1920,"height":19},"deco_rect":{"x":0,"y":0,"width":0,"height":0},"window_rect":{"x":0,"y":0,"width":1920,"height":19},"geometry":{"x":0,"y":1061,"width":3840,"height":19},"name":"i3bar for output VNC-0","window":14680070,"window_properties":{"class":"i3bar","instance":"i3bar","title":"i3bar for output VNC-0","transient_for":null},"nodes":[],"floating_nodes":[],"focus":[],"fullscreen_mode":0,"sticky":false,"floating":"auto_off","swallows":[]}],"floating_nodes":[],"focus":[10891392],"fullscreen_mode":0,"sticky":false,"floating":"auto_off","swallows":[{"dock":3,"insert_where":2}]}],"floating_nodes":[],"focus":[10887648,10886944,10888352],"fullscreen_mode":0,"sticky":false,"floating":"auto_off","swallows":[]}],"floating_nodes":[],"focus":[10886256,10883568],"fullscreen_mode":0,"sticky":false,"floating":"auto_off","swallows":[]}

【问题讨论】:

    标签: python json jq


    【解决方案1】:

    当然,jq 可以做到这一点!

    首先,我们使用递归运算符.. 递归遍历所有内容。然后,我们得到所有的.nodes.floating_nodes,我们使用? 来忽略尝试从数字、字符串或对象获取属性时可能出现的错误。然后,我们用[] 获取这些数组中的所有元素。同样,忽略上一步中的错误留下了一些nulls,因此我们忽略了尝试在nulls 上使用[]? 的错误。最后,我们将所有内容通过管道传输到 select(.window != null),并将每个结果包装在一个数组中以便于处理。

    jq '[.. | .floating_nodes?, .nodes? | .[]? | select(.window != null)]'
    

    【讨论】:

    • 谢谢。麻烦的是,jq 抛出错误:error: Invalid numeric literal (while parsing '..') [.. | .floating_nodes?, .nodes? | .[]? | select(.window != null)] ^^ error: Invalid character 我的 jq 是 1.3 版
    • 我建议为此购买 jq 1.5。如果不是,.. 应在早期版本中定义为 recurse
    【解决方案2】:

    如果window 属性只能出现在nodefloating_node 对象上,您可以通过搜索所有具有window 属性的对象来简化操作。

    .. | objects | select(.window != null)
    

    【讨论】:

    • 谢谢。麻烦的是,jq 抛出错误:错误:对象未定义。 |对象 | select(.window != null) ^^^^^^^ 1 编译错误 我的jq是1.3版
    【解决方案3】:

    解决圣地亚哥的解决方案并尝试回填 jq-1.3 中可能缺少的内容,我得到了这个工作:

    jq 'recurse(.[]) | .floating_nodes, .nodes | .[] | select(.window != null) | select(.focused == true) | .name'
    

    ...它还给出了很多错误消息,例如:

    jq: error: Cannot iterate over number
    jq: error: Cannot iterate over null
    jq: error: Cannot iterate over null
    jq: error: Cannot index number with string
    

    【讨论】:

      【解决方案4】:

      以下内容已使用 jq 1.3、1.4 和 1.5 进行了测试:

      recurse(if type == "object" or type == "array" then .[] else empty end)
      | select(type == "object")
      | ( select(.nodes|type == "array") | .nodes[]),
        ( select(.floating_nodes|type == "array") | .floating_nodes[])
      | select( .window != null ) 
      

      使用给定输入的“完整版”,jq 1.3 产生以下输出:

      {
        "swallows": [],
        "floating": "auto_off",
        "sticky": false,
        "fullscreen_mode": 0,
        "focus": [],
        "floating_nodes": [],
        "nodes": [],
        "window_properties": {
          "transient_for": null,
          "title": "16:11 bhepple:.../~ — Konsole",
          "instance": "konsole",
          "class": "konsole"
        },
        "window": 8388613,
        "name": "16:11 bhepple:.../~ — Konsole",
        "layout": "splith",
        "focused": true,
        "urgent": false,
        "percent": 1,
        "scratchpad_state": "none",
        "orientation": "none",
        "type": "con",
        "id": 10895664,
        "workspace_layout": "default",
        "last_split_layout": "splith",
        "border": "pixel",
        "current_border_width": 1,
        "rect": {
          "height": 1061,
          "width": 1920,
          "y": 0,
          "x": 0
        },
        "deco_rect": {
          "height": 0,
          "width": 0,
          "y": 0,
          "x": 0
        },
        "window_rect": {
          "height": 1059,
          "width": 1918,
          "y": 1,
          "x": 1
        },
        "geometry": {
          "height": 1059,
          "width": 958,
          "y": 0,
          "x": 0
        }
      }
      {
        "swallows": [],
        "floating": "auto_off",
        "sticky": false,
        "fullscreen_mode": 0,
        "focus": [],
        "floating_nodes": [],
        "nodes": [],
        "window_properties": {
          "transient_for": null,
          "title": "i3bar for output VNC-0",
          "instance": "i3bar",
          "class": "i3bar"
        },
        "window": 14680070,
        "name": "i3bar for output VNC-0",
        "layout": "splith",
        "focused": false,
        "urgent": false,
        "percent": 1,
        "scratchpad_state": "none",
        "orientation": "none",
        "type": "con",
        "id": 10891392,
        "workspace_layout": "default",
        "last_split_layout": "splith",
        "border": "pixel",
        "current_border_width": 1,
        "rect": {
          "height": 19,
          "width": 1920,
          "y": 1061,
          "x": 0
        },
        "deco_rect": {
          "height": 0,
          "width": 0,
          "y": 0,
          "x": 0
        },
        "window_rect": {
          "height": 19,
          "width": 1920,
          "y": 0,
          "x": 0
        },
        "geometry": {
          "height": 19,
          "width": 3840,
          "y": 1061,
          "x": 0
        }
      }
      

      【讨论】:

        【解决方案5】:

        这是一个解决方案,它使用 tostream 从给定的输入中扫描 [path,value] 数组流。然后它使用 select 只挑选出以“window”结尾且其对应值不为空的路径。最后,它使用 foreachgetpath 来生成包含对象的流。

        foreach (   tostream
                  | select(length == 2 and .[0][-1] == "window" and .[1] != null)
                ) as $p (
            .
          ; .
          ; getpath($p[0][:-1])
        )
        

        编辑:我现在意识到foreach E as $X (.; .; R) 形式的过滤器几乎总是可以重写为E as $X | R 所以上面真的只是

          ( tostream | select(length == 2 and .[0][-1] == "window" and .[1] != null) ) as $p
        |  getpath($p[0][:-1])
        

        【讨论】:

        • 使用 jq-1.5 给出语法错误:~/ $ i3-msg -t get_tree | jq 'foreach ( tostream | select(length == 2 and .[0][-1] == "window" and .[1] != null) ) as $p ( . ; . ; getpath($p[0][:-1])' jq: error: syntax error, unexpected $end (Unix shell quoting issues?) at <top-level>, line 6: ; getpath($p[0][:-1]) jq: 1 compile error
        • 看起来你还需要一个)在最后一个'之前。
        猜你喜欢
        • 2023-02-01
        • 1970-01-01
        • 2013-03-21
        • 2013-09-11
        • 2013-11-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-05-07
        相关资源
        最近更新 更多