【问题标题】:path syntax for traversing python objects遍历python对象的路径语法
【发布时间】:2020-02-03 11:41:56
【问题描述】:

是否有一种基于表达式的工具来查询 python 复杂对象,就像使用 XPath 查询 XML 或 jsonpath 查询 JSON 一样?

我想过将我的对象序列化为 JSON,然后在其上使用 jsonpath,但这似乎是一种笨拙的方式。

【问题讨论】:

  • “复杂对象”是什么意思?嵌套字典?
  • 我的意思是像 JSON 对象一样复杂的东西:一个可遍历的元素树,可以是 bool、str、int、float 以及 dict(对于对象)和 list(对于数组)。但根元素确实是一本字典。

标签: python xpath jsonpath


【解决方案1】:

在此处添加非库选项。一种基于点符号字符串查找嵌套元素的方法(可以遍历嵌套的dictslists),见下文Gist here

from functools import reduce
import re
from typing import Any, Optional

def find_key(dot_notation_path: str, payload: dict) -> Any:
    """Try to get a deep value from a dict based on a dot-notation"""

    def get_despite_none(payload: Optional[dict], key: str) -> Any:
        """Try to get value from dict, even if dict is None"""
        if not payload or not isinstance(payload, (dict, list)):
            return None
        # can also access lists if needed, e.g., if key is '[1]'
        if (num_key := re.match(r"^\[(\d+)\]$", key)) is not None:
            try:
                return payload[int(num_key.group(1))]
            except IndexError:
                return None
        else:
            return payload.get(key, None)

    found = reduce(get_despite_none, dot_notation_path.split("."), payload)
   
    # compare to None, as the key could exist and be empty
    if found is None:
        raise KeyError()
    return found

# Test cases:

payload = {
    "haystack1": {
        "haystack2": {
            "haystack3": None, 
            "haystack4": "needle"
        }
    },
    "haystack5": [
        {"haystack6": None}, 
        {"haystack7": "needle"}
    ],
    "haystack8": {},
}

find_key("haystack1.haystack2.haystack4", payload)
# "needle"
find_key("haystack5.[1].haystack7", payload)
# "needle"
find_key("[0].haystack5.[1].haystack7", [payload, None])
# "needle"
find_key("haystack8", payload)
# {}
find_key("haystack1.haystack2.haystack4.haystack99", payload)
# KeyError

【讨论】:

    【解决方案2】:

    为了未来的研究人员,我添加了这个答案:

    似乎 jsonpath-rw 是我从一开始就一直在寻找的库,因为它完全符合我最初的要求。

    【讨论】:

      【解决方案3】:

      @vBobCat 我目前正在寻找类似的解决方案。同意使用 json 进行序列化和反序列化并不理想。你最终选择了什么?

      我发现http://objectpath.org/ 接近我的用例的正确解决方案,尽管它缺乏对我需要的字段进行任意更新的功能。它的语法虽然与 JSONPath 略有不同,但表达了 JSONPath 所做的许多事情。

      【讨论】:

      • 您好,抱歉回复晚了。我的用例很简单,所以我使用了Box 库(在接受的答案中)结合exec(),所以我可以在字符串中插入存储的路径并执行它。
      • 再次您好,不知过了这么久对您是否有用,实际上jsonpath-rw 似乎是我们正在寻找的库。
      • 嘿@VBobCat,哎呀,我没想到要更新这个,但是是的,我得出了和你一样的结论。我最初假设该库需要一个 json 字符串作为输入,但结果证明这是不正确的,您可以将字典和列表传递给它。
      【解决方案4】:

      您可以使用内置库 json 将 json 作为嵌套字典导入并使用字典表示法 - root['level1_object']['level2_object'] 遍历它。 JSON 兼容的对象类型当然会作为相应的 Python 类型加载。

      对于其他类型的数据,还有其他库,它们的行为大多类似。

      我最喜欢的是Box,它允许您使用点符号遍历嵌套字典。

      【讨论】:

      • Box 看起来很有希望,我去看看。
      【解决方案5】:

      您可能想看看 AST 模块: https://docs.python.org/2/library/ast.html

      【讨论】:

      • 如何使用 AST 查询“复杂对象”?
      • 我可能误解了它,但似乎我需要将我的根字典序列化为字符串才能使用它。我错过了什么吗?
      猜你喜欢
      • 2016-12-04
      • 1970-01-01
      • 1970-01-01
      • 2015-05-31
      • 2019-04-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多