XML удаляет элементы по совпадению - PullRequest
0 голосов
/ 28 апреля 2018

Мне нравится удалять элементы базы на совпадение подэлементов.
Пример 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). Я хочу удалить все pids, которые соответствуют 302-310. Вот мой код, но я получаю ошибку.

# -*- 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 (элемент)
ValueError: list.remove (x): x отсутствует в списке`

Как решить? Я думаю, что я близко.
Спасибо за просмотр и помощь.

Ответы [ 2 ]

0 голосов
/ 30 апреля 2018

Я хочу удалить все пиды, которые соответствуют 302-310.

Я думаю, что ваша логика регулярных выражений неверна. Если у вас был pidNumber 319 (или 312, 313 и т. Д.), Эти элементы 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, следующий питон выдает тот же вывод 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 (test.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>

Опять же, это дает те же результаты, что и другие опции, использующие тот же ввод.

0 голосов
/ 29 апреля 2018

Вот рабочий код:

enter code hereimport re
from xml.etree import ElementTree as ET

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

for item in getpid:
    pidnum = item.find('.//pidNumber')
    pidnum = pidnum.text
    match = re.findall(regex, pidnum)
    match = ''.join(match)
    if pidnum == match:
        item.clear()
# create a new XML file with the results
tree.write('out.xml')

Спасибо всем.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...