遍历 python 个对象的路径语法

path syntax for traversing python objects

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

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

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

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

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

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

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

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

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

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

在此处添加非库选项。根据点号字符串查找嵌套元素的方法(可以遍历嵌套的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