【问题标题】:python xml query get parentpython xml查询获取父级
【发布时间】:2011-03-21 04:45:47
【问题描述】:

我有一个大的 xml 文档,如下所示:

<Node name="foo">
    <Node name="16764764625">
        <Val name="type"><s>3</s></Val>
        <Val name="owner"><s>1</s></Val>
        <Val name="location"><s>4</s></Val>
        <Val name="brb"><n/></Val>
        <Val name="number"><f>24856</f></Val>
        <Val name="number2"><f>97000.0</f></Val>
    </Node>
    <Node name="1764466544">
        <Val name="type"><s>1</s></Val>
        <Val name="owner"><s>2</s></Val>
        <Val name="location"><s>6</s></Val>
        <Val name="brb"><n/></Val>
        <Val name="number"><f>265456</f></Val>
        <Val name="number2"><f>99000.0</f></Val>
    </Node>
    ...
</Node>

我的任务是通过搜索查找节点 Val name="number" 的子元素是否包含 265456 来获取父节点的值:1764466544(第二个节点中的名称值)

我一直在阅读大量有关 XPath 和 ElementTree 的资料,但我仍然不确定从哪里开始实际查询这个。寻找示例...我找不到任何引用父节点的结果。

python 还是新手.. 任何建议将不胜感激。

谢谢

【问题讨论】:

    标签: python xml xpath


    【解决方案1】:

    不幸的是,在使用 ElementTree API 时,每个 Element 对象都没有对其父对象的引用,因此您无法从已知点向上爬上树。相反,您必须找到可能的父对象并过滤您想要的。

    这通常使用 XPath 表达式来完成。但是,ElementTree 仅支持 XPath 的子集 (see the docs),其中最有用的部分仅在 ElementTree 1.3 中添加,仅在 Python 2.7+ 或 3.2+ 中提供。

    甚至,ElementTree 的 XPath 也无法按原样处理您的文件 - 无法根据节点的文本进行选择,只能根据其属性(或属性值)进行选择。

    我的实验只发现了两种可以继续使用 ElementTree 的方法。如果您使用的是 Python 2.7+(或能够下载并安装较新版本的 ElementTree 以使用较旧的 Python 版本),并且您可以修改 XML 文件的格式以将数字设置为属性,像这样

    <Val name="number"><f val="265456" /></Val>
    

    那么下面的 Python 代码会拉出感兴趣的节点:

    import xml.etree.ElementTree as ETree
    tree = ETree.ElementTree(file='sample.xml')
    nodes = tree.findall(".//Node/Val[@name='number']/f[@val='265456']....")
    

    对于较旧的 Python,或者如果您无法修改 XML 格式,则必须手动过滤无效节点。以下对我有用:

    import xml.etree.ElementTree as ETree
    tree = ETree.ElementTree(file='sample.xml')
    all = tree.findall(".//Node")
    nodes = []
    
    # Filter matching nodes and put them in the nodes variable.
    for node in all:
        for val in node.getchildren():
            if val.attrib['name'] == 'number' and val.getchildren()[0].text =='265456':
                nodes.append(node)
    

    这些解决方案都不是我所说的理想解决方案,但它们是我唯一能够使用 ElementTree 库的解决方案(因为这是您提到的使用方法)。使用第三方库而不是使用内置库可能会更好;有关选项列表,请参阅the Python wiki entry on XMLlxml 是广泛使用的 libxml2 库的 Python 绑定,我建议首先查看它。它支持 XPath,因此您应该能够使用其他答案中的查询。

    【讨论】:

    • Python 添加了一些 XPath 支持真的很烦人,但我不能使用“..”语法从当前节点上升。它应该在 Python documentation 中说明。实际上 documentation 声明支持这种语法。只要您不超过当前元素,它可能就受支持,例如“人/..”?我花了大约一个小时试图弄清楚为什么这不起作用。
    【解决方案2】:

    这个 XPath:

    /Node/Node[Val[@name='number']/f='265456']/@name
    

    输出:

    1764466544
    

    【讨论】:

    • @itwb - 我从来没有在 Python 中尝试过 XPath,所以这部分取决于你,但上面的 XPath 是抽象的。在这里测试一下,例如:xmlme.com/XpathTool.aspx
    • 是的,谢谢。现在我收到此错误:SyntaxError: cannot use absolute path on element.
    • 我在这里不熟悉,但this link 显示以下 XPath 表达式的代码,带有前导 /raise SyntaxError("cannot use absolute path on element")。也许尝试一个相对的表达?这个Node/Node[Val[@name='number']/f='265456']/@name 或者这个//Node/Node[Val[@name='number']/f='265456']/@name
    【解决方案3】:

    以下功能在类似情况下对我有所帮助。正如文档字符串所解释的,它在一般情况下不起作用,但如果您的节点是唯一的,它应该会有所帮助。

    def get_element_ancestry(root, element):
    '''Return a list of ancestor Elements for the given element.
    
    If both root and element are of type xml.etree.ElementTree.Element, and if
    the given root contains the given element as a descendent, then return a
    list of direct xml.etree.ElementTree.Element ancestors, starting with root
    and ending with element. Otherwise, return an empty list.
    
    The xml.etree.ElementTree module offers no function to return the parent of
    a given Element, presumably because an Element may be in more than one tree,
    or even multiple times within a given tree, so its parent depends on the
    context. This function provides a solution in the specific cases where the
    caller either knows that the given element appears just once within the
    tree or is satisfied with the first branch to reference the given element.
    '''
    result = []
    xet = xml.etree.ElementTree
    if not xet.iselement(root) or not xet.iselement(element):
        return result
    xpath = './/' + element.tag \
        + ''.join(["[@%s='%s']" % a for a in element.items()])
    parent = root
    while parent != None:
        result.append(parent)
        for child in parent.findall('*'):
            if child == element:
                result.append(element)
                return result
            if child.findall(xpath).count(element):
                parent = child
                break
        else:
            return []
    return result
    

    【讨论】:

    • OP 已有 3 年以上的历史了... 最好澄清一下您的答案现在是否真的有效,使用当前版本,以前可以使用,使用旧版本,或者您认为相关的任何东西知道这一点.
    【解决方案4】:

    通常

    node.parentNode 
    

    将返回一个指向父节点的指针(使用 DOM 解析器时)。

    XPath 见

    http://www.tizag.com/xmlTutorial/xpathparent.php

    【讨论】:

    • 这不适用于 ElementTree,在任何版本的库中都没有这样的属性。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-01-31
    • 1970-01-01
    • 2013-10-03
    • 2019-04-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多