遍历 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 是我从一开始就在寻找的库,因为它完全符合我最初的要求。
在此处添加非库选项。根据点号字符串查找嵌套元素的方法(可以遍历嵌套的dicts
和lists
),见下文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
是否有一种基于表达式的工具来查询 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 是我从一开始就在寻找的库,因为它完全符合我最初的要求。
在此处添加非库选项。根据点号字符串查找嵌套元素的方法(可以遍历嵌套的dicts
和lists
),见下文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