【问题标题】:Python xml etree find parent node by text of childPython xml etree通过子文本查找父节点
【发布时间】:2017-02-23 17:04:59
【问题描述】:

我有一个这样的 XML

<xml>
    <access>
        <user>
            <name>user1</name>
            <group>testgroup</group>
        </user>
        <user>
            <name>user2</name>
            <group>testgroup</group>
        </user>
    <access>
</xml>

我现在想将&lt;group&gt;testgroup2&lt;/group&gt; 添加到 user1 子树。

使用以下我可以得到名称

access = root.find('access')
name = [element for element in access.iter() if element.text == 'user1']

但我无法使用它告诉我的 name.find('..') 访问父级

AttributeError: 'list' object has no attribute 'find'.

是否有可能访问名称中的文本为“user1”的&lt;access&gt; 的确切&lt;user&gt; 子级?

预期结果:

<xml>
    <access>
        <user>
            <name>user1</name>
            <group>testgroup</group>
            <group>testgroup2</group>
        </user>
        <user>
            <name>user2</name>
            <group>testgroup</group>
        </user>
    <access>
</xml>

重要提示:我不能使用 lxml 来使用 getparent() 方法,我被困在 xml.etree

【问题讨论】:

  • 您已将name 设置为列表,因此当然没有find 属性,因为列表对象没有该方法。
  • 我知道。这就是错误消息显然告诉我的内容。但是我该如何解决我的问题呢?
  • 向我们展示您预期的 XML 结果。
  • 我不知道,但我想我已经写了我想添加的内容。不过,我已经用我的预期结果更新了这个问题。

标签: python xml


【解决方案1】:

为此,使用“查找”,您需要这样做:for ele in name: ele.find('..') # 将ele作为元素访问

【讨论】:

  • 我不想访问ele。我需要 ele.getParent() 但这在 xml.etree 中不存在: 是父级,我想找到 并附加到 另一个 条目。
【解决方案2】:

这就是我解决这个问题的方法,如果有人有兴趣在 xml 而不是 lxml 中做这些事情(为什么)。

根据

的建议

http://effbot.org/zone/element.htm#accessing-parents

import xml.etree.ElementTree as et

tree = et.parse(my_xmlfile)
root = tree.getroot()
access = root.find('access')

# ... snip ...

def iterparent(tree):
    for parent in tree.getiterator():
        for child in parent:
            yield parent, child

# users = list of user-names that need new_group added
# iter through tupel and find the username
# alter xml tree when found

for user in users:
    print "processing user: %s" % user
    for parent, child in iterparent(access):
        if child.tag == "name" and child.text == user:
            print "Name found: %s" % user
            parent.append(et.fromstring('<group>%s</group>' % new_group))

在此 et.dump(tree) 显示树现在包含正确更改的用户子树并添加了另一个组标签之后。

注意:我不确定为什么会这样,我只是希望 yield 提供对树的引用,因此更改返回的父级 yield 会更改原始树。我的python知识不足以确定这一点。我只知道这样对我有用。

【讨论】:

    【解决方案3】:

    您可以编写递归方法来遍历树并捕获父级。

    def recurse_tree(node):
        for child in node.getchildren():
            if child.text == 'user1':
                yield node
            for subchild in recurse_tree(child):
                yield subchild
    
    print list(recurse_tree(root)) 
    # [<Element 'user' at 0x18a1470>]
    

    如果您使用的是 Python 3.X,则可以使用漂亮的 yield from ... 语法,而不是遍历递归调用。

    请注意,这可能会多次生成同一个节点(如果有多个包含目标文本的子节点)。您可以使用集合来删除重复项,也可以更改控制流以防止这种情况发生。

    【讨论】:

    • 在这种情况下,“node”将保存父级,因此我可以在 if 语句中使用 node.append(et.fromstring(...ect ect 等) 访问它,对吗?我已经阅读过有关 yield但还没有完全理解它在内部是如何工作的......
    • @dreamyrhodes 是的,node 将持有父母。解释 yield 超出了注释的范围,但基本上它是在函数中构造父元素列表的替代方法。
    • 是的,评论当然太多了。我只是对这有效(以及下面我自己的解决方案)感到困惑,并且产生树的对象然后更改它们会改变原始的 xml 树。无论如何也要感谢您的建议比我的解决方案短。
    【解决方案4】:

    您可以直接使用findall() 方法获取匹配name='user1' 的父节点。见下面代码

    import xml.etree.ElementTree as ET
    tree = ET.parse('test.xml') #build tree object using your xml
    root = tree.getroot() #using tree object get the root
    
    for parent in root.findall(".//*[name='user1']"): 
        # the predicate [name='user1'] preceded by asterisk will give 
        # all elements where child having name='user1'
        parent.append(ET.fromstring("<group>testgroup2</group>"))
    
    
    # if you want to see the xml after adding the string
    ET.dump(root) 
    # optionally to save the xml
    tree.write('output.xml')
    

    【讨论】:

    • 单独粘贴代码无济于事。请简要说明它的作用?
    • 用cmets强调代码
    猜你喜欢
    • 2014-04-25
    • 2019-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-09
    相关资源
    最近更新 更多