python - 基于匹配的 XML 删除元素

标签 python xml parsing

我喜欢根据子元素匹配删除元素。
file.xml 示例:

 <entry>
  <title>TEST1</title>
  <profile>
    <title>Default</title>
    <pid>
      <pidNumber>1880</pidNumber>
      <ContentType>PMT</ContentType>
      <isScrambled>0</isScrambled>
    </pid>
    <pid>
      <pidNumber>201</pidNumber>
      <ContentType>Video</ContentType>
      <isScrambled>0</isScrambled>
    </pid>
    <pid>
      <pidNumber>301</pidNumber>
      <ContentType>Audio</ContentType>
      <isScrambled>0</isScrambled>
    </pid>
    <pid>
      <pidNumber>302</pidNumber>
      <ContentType>Audio</ContentType>
      <isScrambled>0</isScrambled>
    </pid>
    <pid>
      <pidNumber>310</pidNumber>
      <ContentType>Audio</ContentType>
      <isScrambled>0</isScrambled>
    </pid>
  </profile>
</entry>

如您所见,有很多 PIDS 值 (201,301,302-310),我想删除所有与 302-310 匹配的 pids。这是我的代码,但出现错误。

# -*- coding: utf-8 -*-
import re
from xml.etree import ElementTree as ET

root = ET.parse("file.xml").getroot()
regex = r"[3][0-1][02-9]"
getpid = root.iter("pid")

for item in getpid:
    pidnum = item.find('.//pidNumber')
    pidnum = pidnum.text
    match = re.findall(regex, pidnum)
    match = ''.join(match)
    if pidnum == match:
        ET.dump(item)
        item.remove(getpid)

tree = ET(root)
tree.write("out.xml")

我得到的错误:

self._children.remove(element)
ValueError: list.remove(x): x not in list`

如何解决?我想我很接近。
感谢您的查看和帮助。

最佳答案

I want to remove all the pids that matches from 302-310.

我认为您的正则表达式逻辑有缺陷。如果您的 pidNumber 是 319(或 312313 等),这些 pid 元素也将被删除。

此外,您的代码并没有完全删除 pid,而是删除了它的子元素,留下一个空的 pid 元素。 (也许这是需要的,但它听起来不像是基于“我喜欢根据子元素匹配删除元素。”。)

不使用 getroot(),尝试使用 find() 获取 profile 元素。这是 pid 的父级,这是我们删除 pid 本身所需要的。

而不是使用正则表达式来匹配 pidNumber,只需进行基本比较即可。

例子...

file.xml(添加了额外的 pid 元素用于测试)

<entry>
    <title>TEST1</title>
    <profile>
        <title>Default</title>
        <pid>
            <pidNumber>1880</pidNumber>
            <ContentType>PMT</ContentType>
            <isScrambled>0</isScrambled>
        </pid>
        <pid>
            <pidNumber>201</pidNumber>
            <ContentType>Video</ContentType>
            <isScrambled>0</isScrambled>
        </pid>
        <pid>
            <pidNumber>301</pidNumber>
            <ContentType>Audio</ContentType>
            <isScrambled>0</isScrambled>
        </pid>
        <pid>
            <pidNumber>302</pidNumber>
            <ContentType>Audio</ContentType>
            <isScrambled>0</isScrambled>
        </pid>
        <pid>
            <pidNumber>303</pidNumber>
            <ContentType>Audio</ContentType>
            <isScrambled>0</isScrambled>
        </pid>
        <pid>
            <pidNumber>309</pidNumber>
            <ContentType>Audio</ContentType>
            <isScrambled>0</isScrambled>
        </pid>
        <pid>
            <pidNumber>310</pidNumber>
            <ContentType>Audio</ContentType>
            <isScrambled>0</isScrambled>
        </pid>
        <pid>
            <pidNumber>319</pidNumber>
            <ContentType>Audio</ContentType>
            <isScrambled>0</isScrambled>
        </pid>
    </profile>
</entry>

python

from xml.etree import ElementTree as ET

tree = ET.parse("file.xml")
profile = tree.find("profile")

for pid in profile.findall(".//pid"):
    nbr = int(pid.find("pidNumber").text)
    if 302 <= nbr <= 310:
        profile.remove(pid)

tree.write('out.xml')

out.xml

<entry>
    <title>TEST1</title>
    <profile>
        <title>Default</title>
        <pid>
            <pidNumber>1880</pidNumber>
            <ContentType>PMT</ContentType>
            <isScrambled>0</isScrambled>
        </pid>
        <pid>
            <pidNumber>201</pidNumber>
            <ContentType>Video</ContentType>
            <isScrambled>0</isScrambled>
        </pid>
        <pid>
            <pidNumber>301</pidNumber>
            <ContentType>Audio</ContentType>
            <isScrambled>0</isScrambled>
        </pid>
        <pid>
            <pidNumber>319</pidNumber>
            <ContentType>Audio</ContentType>
            <isScrambled>0</isScrambled>
        </pid>
    </profile>
</entry>

另一种选择是使用 lxml 而不是 ElementTree。这将为您提供完整的 xpath 支持,以便您可以在谓词中进行比较。

使用上面的 file.xml 输入,下面的 python 生成与上面相同的 out.xml 输出。

from lxml import etree

tree = etree.parse("file.xml")
for pid in tree.xpath(".//pid[pidNumber[. >= 302][310 >= .]]"):
    pid.getparent().remove(pid)

tree.write("out.xml")

第三种选择是使用 XSLT(感谢@Parfait 的建议)...

python

from lxml import etree

tree = etree.parse("file.xml")
xslt = etree.parse("test.xsl")
new_tree = tree.xslt(xslt)
new_tree.write_output("out_xslt.xml")

XSLT 1.0 (测试.xsl)

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="pid[pidNumber[. >= 302][310 >= .]]"/>

</xsl:stylesheet>

同样,这会产生与使用相同输入的其他选项相同的结果。

关于python - 基于匹配的 XML 删除元素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50078222/

相关文章:

c++ - 解析一个奇怪的字符串C++

java - 如何解决通用实例化问题?

javascript - 在 typescript 中解析JSON数组

android - 需要 Android 应用程序的工作流程创意

python - SQL Alchemy 参数化查询,绑定(bind)表名作为参数给出错误

python - 无法使用 Python 连接 Unix 域套接字 : No such file or directory

Python快速更改8KK行文件中的300K行

java - 解析 android 屏幕转储文件

ruby-on-rails - ruby rails : Using XML Builder Partials

python - 尝试使用 TensorBoard 时出现 AttributeError : module 'tensorboard.util' has no attribute 'PersistentOpEvaluator' ,