【问题标题】:Deleting multiple XML elements between too elements删除too元素之间的多个XML元素
【发布时间】:2016-03-04 04:53:48
【问题描述】:

我有一些如下所示的 XML:

<FirstLevel>
    <item1>Val1</item1>
    <item2>Val2</item2>
    <item3>Val3</item3>
    <item4>Val4</item4>
    <item5>Val5</item5>
</FirstLevel>

使用 Python,我想删除 item1item5 之间的所有项目,而无需明确命名,以获得以下结果:

<FirstLevel>
    <item1>Val1</item1>
    <item5>Val5</item5>
</FirstLevel>

使用lxml,我知道如何找到item1item5,所以我只需要知道如何构建介于这两者之间的某种XML 元素列表。

【问题讨论】:

  • 在 Python 中,读取文件、删除所需部分然后重写文件通常要容易得多。您使用的模块可能会有所不同,idk。
  • 感谢您的快速回答。就我而言,在更大的用例中访问该文件会很困难,但如果我找不到其他解决方案,我会考虑它:)

标签: python xml xml-parsing lxml elementtree


【解决方案1】:

您可以使用preceding-siblingfollowing-sibling 的组合,例如:

from lxml.etree import fromstring, tostring

data = """<FirstLevel>
    <item1>Val1</item1>
    <item2>Val2</item2>
    <item3>Val3</item3>
    <item4>Val4</item4>
    <item5>Val5</item5>
</FirstLevel>
"""

tree = fromstring(data)
node_start = "item1"
node_end = "item5"

parent = tree.xpath("//FirstLevel")[0]
for node in parent.xpath("*[preceding-sibling::%s and following-sibling::%s]" % (node_start, node_end)):
    parent.remove(node)

print(tostring(tree))

打印:

<FirstLevel>
    <item1>Val1</item1>
    <item5>Val5</item5>
</FirstLevel>

如果您可以在单个节点内多次出现item1item5

item_start = "item1"
item_end = "item5"

parent = tree.xpath("//FirstLevel")[0]
for node_start in parent.xpath("%s" % item_start):
    for node in node_start.xpath("following-sibling::%s" % item_end):
        parent.remove(node)

【讨论】:

  • 非常感谢您的回答,它在所描述的情况下非常有效。但是,如果我们考虑重复两次“itemN”元素的情况(让 item1 到 item5,然后再从 item1 到 item5),它将删除第一个 item1 和最后一个 item5 之间的所有元素。我怎样才能再次获得 item1、item5、item1 和 item5。我希望它很清楚:)
  • @filaton 我想我明白你的意思。请查看更新。谢谢。
【解决方案2】:

感谢 alecxe,我找到了解决方案。如果我们有 item1-item5 元素的多个实例,他的回答非常适合所描述的案例,但不起作用(即使他的更新)(请参阅我对他的回答的评论以更好地理解)。

无论如何,我找到了另一种解决方案(我认为它更简单,更 Pythonic):

from lxml.etree import fromstring, tostring

data = """<FirstLevel>
    <item1>Val1</item1>
    <item2>Val2</item2>
    <item3>Val3</item3>
    <item4>Val4</item4>
    <item5>Val5</item5>
    <item1>Val1</item1>
    <item2>Val2</item2>
    <item3>Val3</item3>
    <item4>Val4</item4>
    <item5>Val5</item5>
</FirstLevel>
"""

tree = fromstring(data)

item1_list = tree.findall("item1")

for item1 in item1_list:
    next_node = item1.getnext()
    while next_node.tag != "item5":
        tree.remove(next_node)
        next_node = item1.getnext()

print(tostring(tree))

还有一个来自 alecxe 评论的解决方案对我有用:

从 lxml.etree 导入 fromstring, tostring

data = """<FirstLevel>
    <item1>Val1</item1>
    <item2>Val2</item2>
    <item3>Val3</item3>
    <item4>Val4</item4>
    <item5>Val5</item5>
    <item1>Val1</item1>
    <item2>Val2</item2>
    <item3>Val3</item3>
    <item4>Val4</item4>
    <item5>Val5</item5>
    <item1>Val1</item1>
    <item2>Val2</item2>
    <item3>Val3</item3>
    <item4>Val4</item4>
    <item5>Val5</item5>
</FirstLevel>
"""

tree = fromstring(data)
node_start = "item1"
node_end = "item5"

parent = tree.xpath("//FirstLevel")[0]
# Remove first section
for node in parent.xpath("*[(preceding-sibling::item1)[1] and (following-sibling::item5)[3]]"):
    parent.remove(node)
# Remove second section
for node in parent.xpath("*[(preceding-sibling::item1)[2] and (following-sibling::item5)[2]]"):
    parent.remove(node)
# Remove last section
for node in parent.xpath("*[(preceding-sibling::item1)[3] and (following-sibling::item5)[last()]]"):
    parent.remove(node)

print(tostring(tree))

通过尝试多个值,我找到了放入 preceding-following-sibling 的正确索引,但仍然没有真正理解其背后的逻辑,但它至少对我有用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-08-14
    • 2021-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-09
    相关资源
    最近更新 更多