Как удалить родительский узел в файле XML на основе текста в дочернем элементе? Использование Python 3.6 - PullRequest
1 голос
/ 25 апреля 2020

Сейчас я пытаюсь использовать l xml в Python 3.6. Я хочу удалить «Программы», если они содержат хеджирование, и вообще удалить «Запрос», если ни одна из программ не содержит «keep». xml имеет следующую структуру:

<Requests>
   <Request>
        <ProgramSelection>
            <Program> <![CDATA[hedge]]> </Program>
            <Program> <![CDATA[keep]] </Program>
        </ProgramSelection>
    </Request>
</Requests>
import lxml.etree


file_name = r'C:filename.xml'
parser = lxml.etree.XMLParser(strip_cdata=False)
tree = lxml.etree.parse(file_name, parser)
root = tree.getroot()

for elem in tree.xpath("./Request[ProgramSelection/Program='hedge']"):
    root.remove(elem)

Ответы [ 2 ]

1 голос
/ 25 апреля 2020

Поскольку вы используете модуль lxml, рассмотрим XSLT , язык специального назначения, предназначенный для преобразования файлов XML. При таком подходе петли for или if logi c не требуются. Кроме того, XSLT является переносимым, поэтому его можно запускать гораздо дальше Python.

Следующий скрипт запускает Identity Transform для копирования документа как есть, а затем запускает два пустых шаблона в нужной логике c чтобы удалить их содержимое.

XSLT (сохранить как файл .xsl)

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

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

  <xsl:template match="text()">
      <xsl:value-of select='normalize-space()'/>
  </xsl:template>

  <xsl:template match="Program[contains(text(),'hedge')]"/>
  <xsl:template match="Request[not(contains(., 'keep'))]"/>

</xsl:stylesheet>

Python

import lxml.etree as et

doc = et.parse('Input.xml')
xsl = et.parse('XSLT_Script.xsl')

transform = et.XSLT(xsl)    
result = transform(doc)

# OUTPUT TO SCREEN
print(result)

# OUTPUT TO FILE
with open('Output.xml', 'wb') as f:
    f.write(result)

Выход

<?xml version="1.0"?>
<Requests>
  <Request>
    <ProgramSelection>
      <Program>keep</Program>
    </ProgramSelection>
  </Request>
</Requests>

Online Demo

1 голос
/ 25 апреля 2020

Вы рядом. Следующие два xpath выбирают элементы, соответствующие вашим критериям удаления

import lxml.etree

file_name = r'test.xml'
parser = lxml.etree.XMLParser(strip_cdata=False)
tree = lxml.etree.parse(file_name, parser)
root = tree.getroot()

# remove <Request> lacking a <Program>keep</Program>
for request in tree.xpath(
        "Request[not(ProgramSelection/Program[contains(text(),keep)])]"):
    request.getparent().remove(request)

# remove <Program>hedge</Program>
for program in tree.xpath(
        "Request/ProgramSelection/Program[contains(text(), hedge)]"):
    program.getparent().remove(program)

print(lxml.etree.tostring(tree, pretty_print=True).decode())

И вы можете объединить их в немного менее читаемый "или"

import lxml.etree

file_name = r'test.xml'
parser = lxml.etree.XMLParser(strip_cdata=False)
tree = lxml.etree.parse(file_name, parser)
root = tree.getroot()

# remove <Request> lacking a <Program>keep</Program>
# remove <Program>hedge</Program>
for elem in tree.xpath("Request[
        not(ProgramSelection/Program[contains(text(),keep)])]"
        "|"        
        "Request/ProgramSelection/Program[contains(text(), hedge)]"):
    elem.getparent().remove(elem)

print(lxml.etree.tostring(tree, pretty_print=True).decode())
...