Python 3 XML 如果找不到另一个元素,则复制并重命名元素

Python 3 XML Duplicate and rename element if another element is not found

我有一个 IN.xml 文件要修复,这让我很头疼:)

要解决的问题是在 IN.xml 文件中查找 "Donnees_Releve" 元素中是否至少存在一个 "Classe_Temporelle_Distributeur"。

如果没有,则复制一个 "Classe_Temporelle" 元素(始终存在)并将其重命名为 "Classe_Temporelle_Distributeur"。

下面的示例带有 IN.xml 和预期的 OUT.xml 文件

这是 IN.xml 文件的样子

<filename>
 <prm>
  <Donnees_Releve>
   <Classe_Temporelle>
    <data></data>
   </Classe_Temporelle>
   <Classe_Temporelle>
    <data></data>
   </Classe_Temporelle>
  </Donnees_Releve>
 <Donnees_Releve>
   <Classe_Temporelle>
    <data></data>
   </Classe_Temporelle>
   <Classe_Temporelle>
    <data></data>
   </Classe_Temporelle>
  </Donnees_Releve> 
 </prm>
<filename>

这是预期的 OUT.xml 文件

注意:如果元素 "Classe_Temporelle_Distributeur" 不存在于 "Donnees_Releve" 节点中,则添加该元素,它是 Classe_Temporelle 节点的副本。

每个元素可以有 1-n 个子元素 每个 Classe_Temporelle 应该有一个对应的 Classe_Temporelle_Distributeur,(每个 Classe_Temporelle 不包含相同的数据。)

<filename>
     <prm>
      <Donnees_Releve>
       <Classe_Temporelle>
        <data></data>
       </Classe_Temporelle>
       <Classe_Temporelle>
        <data></data>
       </Classe_Temporelle>
       <Classe_Temporelle_Distributeur>
        <data></data>
       </Classe_Temporelle_Distributeur>
       <Classe_Temporelle_Distributeur>
        <data></data>
       </Classe_Temporelle_Distributeur>
      </Donnees_Releve>
      <Donnees_Releve>
       <Classe_Temporelle>
        <data></data>
       </Classe_Temporelle>
       <Classe_Temporelle>
        <data></data>
       </Classe_Temporelle>
       <Classe_Temporelle_Distributeur>
        <data></data>
       </Classe_Temporelle_Distributeur>
       <Classe_Temporelle_Distributeur>
        <data></data>
       </Classe_Temporelle_Distributeur>
      </Donnees_Releve> 
     </prm>
    <filename>

我写的代码

部分工作,但它只修复了每个 Donnees_Releve 的第一个元素 "Classe_Temporelle"。但是它可以有很多子元素然后它不匹配请求

import xml.etree.ElementTree as ET


file = 'IN/IN.xml'

tree = ET.parse(file)
root = tree.getroot()

#loop on each PRM
for prm in root.iter('PRM'):


    # Loop on each Donnees_Releve

    for classeDistributeur in prm.iter('Donnees_Releve'):

        Classe_Temporelle_Distributeur = classeDistributeur.find('Classe_Temporelle_Distributeur')

        if Classe_Temporelle_Distributeur is None:
            print("Classe_Temporelle_Distributeur not found")

            # copy element Classe_Temporelle in Classe_Temporelle_Distributeur

            Classe_Temporelle = classeDistributeur.find('Classe_Temporelle')

            dupe = copy.deepcopy(Classe_Temporelle) #copy node
            classeDistributeur.append(dupe) #insert the new node

            # Rename Node
            Classe_Temporelle.tag = "Classe_Temporelle_Distributeur"


        else :
            print("Ok nothing to do")


tree.write('OUT/out.xml')

你能帮帮我吗?

请考虑 XSLT, the special purpose language designed to transform XML files, and avoid any procedural XML mappings at the general purpose level here being Python. While Python's built-in module, etree, does not support XSLT, it's third-party module, lxml 确实支持 XSLT 1.0 和完整的 XPath 1.0。

或者,Python 调用任何外部 XSLT 1.0 - 3.0 processor,如 Saxon 或 Xalan,甚至使用任何其他通用语言 运行 XSLT 转换(即 Java , Javascript, C#, C++, PHP, Perl, R, VB) 因为每个都有自己的 XSLT 库。

扩展您的示例以突出前三位 Python and XSLT Whosebug 金徽章用户,XSLT 可以使用多种模板模式轻松复制所需的节点。

XSLT (另存为.xsl文件,一个特殊的.xml文件)

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

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

  <xsl:template match="Donnees_Releve">
    <xsl:copy>
      <xsl:apply-templates select="Classe_Temporelle" mode="t1"/>
      <xsl:apply-templates select="Classe_Temporelle" mode="t2"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="Classe_Temporelle" mode="t1">
    <xsl:copy>
      <xsl:apply-templates select="data" />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="Classe_Temporelle" mode="t2">
    <Classe_Temporelle_Distributeur>
      <xsl:apply-templates select="data" />
    </Classe_Temporelle_Distributeur>
  </xsl:template>

</xsl:stylesheet>

输入XML

<?xml version="1.0" encoding="utf-8" ?>
<filename>
 <prm>
  <Donnees_Releve>
   <Classe_Temporelle>
    <data>Martijn Pietersr</data>
   </Classe_Temporelle>
   <Classe_Temporelle>
    <data>Alex Martelli</data>
   </Classe_Temporelle>
   <Classe_Temporelle>
    <data>unutbu</data>
   </Classe_Temporelle>
  </Donnees_Releve>
 <Donnees_Releve>
   <Classe_Temporelle>
    <data>Dimitre Novatchev</data>
   </Classe_Temporelle>
   <Classe_Temporelle>
    <data>Martin Honnen</data>
   </Classe_Temporelle>
   <Classe_Temporelle>
    <data>Michael Kay</data>
   </Classe_Temporelle>
  </Donnees_Releve> 
 </prm>
</filename>

Python(无for循环或if逻辑)

import lxml.etree as et

# INPUT XML AND XSL SOURCES
xml = et.parse('Input.xml')
xsl = et.parse('Script.xsl')

# RUN TRANSFORMATION
transformer = et.XSLT(xsl)
new_xml = transformer(xml)

# PRINT TO CONSOLE
print(new_xml)

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

输出XML

<?xml version="1.0" encoding="utf-16"?>
<filename>
  <prm>
    <Donnees_Releve>
      <Classe_Temporelle>
        <data>Martijn Pietersr</data>
      </Classe_Temporelle>
      <Classe_Temporelle>
        <data>Alex Martelli</data>
      </Classe_Temporelle>
      <Classe_Temporelle>
        <data>unutbu</data>
      </Classe_Temporelle>
      <Classe_Temporelle_Distributeur>
        <data>Martijn Pietersr</data>
      </Classe_Temporelle_Distributeur>
      <Classe_Temporelle_Distributeur>
        <data>Alex Martelli</data>
      </Classe_Temporelle_Distributeur>
      <Classe_Temporelle_Distributeur>
        <data>unutbu</data>
      </Classe_Temporelle_Distributeur>
    </Donnees_Releve>
    <Donnees_Releve>
      <Classe_Temporelle>
        <data>Dimitre Novatchev</data>
      </Classe_Temporelle>
      <Classe_Temporelle>
        <data>Martin Honnen</data>
      </Classe_Temporelle>
      <Classe_Temporelle>
        <data>Michael Kay</data>
      </Classe_Temporelle>
      <Classe_Temporelle_Distributeur>
        <data>Dimitre Novatchev</data>
      </Classe_Temporelle_Distributeur>
      <Classe_Temporelle_Distributeur>
        <data>Martin Honnen</data>
      </Classe_Temporelle_Distributeur>
      <Classe_Temporelle_Distributeur>
        <data>Michael Kay</data>
      </Classe_Temporelle_Distributeur>
    </Donnees_Releve>
  </prm>
</filename>

Online Demo

您的代码的问题是您调用 find,它只查找 具有给定名称的元素的 第一次 出现。 您应该改用 findall 和一个循环。

另一个(可选)更正是使用较短的名称。

因此请更改您的代码,例如至:

for dr in root.iter('Donnees_Releve'):
    if dr.find('Classe_Temporelle_Distributeur') is None:
        for ct in dr.findall('Classe_Temporelle'):
            wrk = copy.deepcopy(ct)
            wrk.tag = 'Classe_Temporelle_Distributeur'
            dr.append(wrk)

恐怕其他解决方案会复制现有的 Classe_Temporelle Classe_Temporelle_Distributeur 的元素,无论这 元素存在(在这种情况下不应进行复制)。