【问题标题】:Need help replacing text in a sub-element with text from multiple sub-elements in an XML需要帮助用 XML 中多个子元素中的文本替换子元素中的文本
【发布时间】:2019-01-16 06:30:58
【问题描述】:

我正在尝试将 XML 树中的值文本替换为树中其他子元素中的文本。我是 Python 新手,需要一些关于如何编写的帮助。

我的 XML 示例,其中一些元素因长度而被省略:

<SalesOrder>
  <SalesOrderLines>
    <SalesOrderLine>
      <Item>
        <LineNo>1</LineNo>
        <Quantity>4.00</Quantity>
      </Item>
      <ConfigurationDetails>
         <ConfigurationDetail>
          <ConfigurationAttribute>
            <Name>ConfigurationModel</Name>
            <Value>HV</Value>
          </ConfigurationAttribute>
          <ConfigurationAttribute>
            <Name>EXWidth</Name>
            <Value>59.5</Value>
          </ConfigurationAttribute>
          <ConfigurationAttribute>
            <Name>EXHeight</Name>
            <Value>59.5</Value>
          </ConfigurationAttribute>
          <ConfigurationAttribute>
            <Name>Handing</Name>
            <Value>XO</Value>
          </ConfigurationAttribute>
          <ConfigurationAttribute>
            <Name>LongDescription</Name>
            <Value>This is a long paragraph of text i want to replace with 
            the above text for the Value sub-element</Value>
          </ConfigurationAttribute>
        </ConfigurationDetail>
      </ConfigurationDetails>
    </SalesOrderLine>
  </SalesOrderLines>
</SalesOrder>

这是我第一次尝试使用 ElementTree 库的 Python 代码:

import xml.etree.ElementTree as ET

from tkinter import Tk
from tkinter.filedialog import askopenfilename, asksaveasfilename

Tk().withdraw()
file = askopenfilename()

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

def model():
    for ConfigurationAttribute in root.iter('ConfigurationAttribute'):
        descrip = ConfigurationAttribute.find('Name').text
        model = ''

        if descrip == 'ConfigurationModel':
            model = ConfigurationAttribute.find('Value').text

def handing():
    for ConfigurationAttribute in root.iter('ConfigurationAttribute'):
        descrip = ConfigurationAttribute.find('Name').text
        handing = ''

        if descrip == 'Handing' and ConfigurationAttribute.find('Value') is 
not None:
            handing = ConfigurationAttribute.find('Value').text

def width():
    for ConfigurationAttribute in root.iter('ConfigurationAttribute'):
        descrip = ConfigurationAttribute.find('Name').text
        width = ''

        if descrip == 'EXWidth':
            width = ConfigurationAttribute.find('Value').text

def height():
    for ConfigurationAttribute in root.iter('ConfigurationAttribute'):
        descrip = ConfigurationAttribute.find('Name').text
        height = ''

         if descrip == 'EXHeight':
            height = ConfigurationAttribute.find('Value').text

for ConfigurationAttribute in root.iter('ConfigurationAttribute'):
    descrip = ConfigurationAttribute.find('Name').text

    if descrip == 'LongDescription':
        model()
        handing()
        width()
        height()        
        ConfigurationAttribute.find('Value').text = str(model), str(handing), 
            str(width), '" x ', str(height), '"' 

tree.write(asksaveasfilename(defaultextension='.xml',))

这会输出错误。我正在寻找的是要替换为来自 ConfigurationModel、Handing、EXWidth 和 EXHeight Name 子元素的 Value 子元素文本的 Value 子元素中的文本段落,如下所示:

<ConfigurationAttribute>
    <Name>LongDescription</Name>
    <Value> HV, XO, 59.5" x 59.5"</Value>
</ConfigurationAttribute>

以下是我在运行代码时收到的错误:

Traceback(最近一次调用最后一次): 文件“\app\users\Home\natep\Documents\NP\py\PrestoParse.py”,第 59 行,在 tree.write(asksaveasfilename(defaultextension='.xml',)) 文件“C:\Users\natep.RANDK\AppData\Local\Programs\Python\Python37-32\lib\xml\etree\ElementTree.py”,第 777 行,写入 short_empty_elements=short_empty_elements) _serialize_xml 中的文件“C:\Users\natep.RANDK\AppData\Local\Programs\Python\Python37-32\lib\xml\etree\ElementTree.py”,第 942 行 short_empty_elements=short_empty_elements) _serialize_xml 中的文件“C:\Users\natep.RANDK\AppData\Local\Programs\Python\Python37-32\lib\xml\etree\ElementTree.py”,第 942 行 short_empty_elements=short_empty_elements) _serialize_xml 中的文件“C:\Users\natep.RANDK\AppData\Local\Programs\Python\Python37-32\lib\xml\etree\ElementTree.py”,第 942 行 short_empty_elements=short_empty_elements) [上一行再重复 3 次] _serialize_xml 中的文件“C:\Users\natep.RANDK\AppData\Local\Programs\Python\Python37-32\lib\xml\etree\ElementTree.py”,第 939 行 写(_escape_cdata(文本)) TypeError: write() 参数必须是 str,而不是 tuple

在输出文件中,我尝试更改的 Value 子元素是空的,没有结束标记,并且现在删除了所有超过它的内容。

【问题讨论】:

  • 欢迎来到 SO!你看到了什么确切的错误? (通常,如果您的问题涉及错误,最好将错误包含在您的问题中。)
  • 你可以使用像lxml这样的非内置库吗?此外,XML 中存在拼写错误 (SalesOderLine) 和未关闭的节点 (ConfigurationDetail)。
  • 感谢您抽出宝贵时间查看此内容。我已包含错误并编辑了 XML 中的拼写错误并添加到 ConfigurationDetail 的结束标记。我可以使用lxml,如果这是我想要在这里实现的更好的解决方案。

标签: python xml python-3.x xml-parsing elementtree


【解决方案1】:

考虑XSLT,这是一种专门用于转换 XML 文件的语言。 Python 的第三方模块 lxml 可以运行 XSLT 1.0 脚本(不是内置的 etree),并且无需单个循环即可运行。

具体来说,XSLT 脚本运行Identity Transform 以按原样复制整个文档。然后,脚本调整最后一个 Value 节点,方法是使用条件 XPath(与 XSLT 的同级)表达式提取前面的兄弟节点,最后将文本值与逗号分隔符和所需的引号连接在一起。

XSLT (另存为.xsl文件,下面Python要加载的特殊.xml文件)

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes"/>

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

    <xsl:template match="ConfigurationAttribute[Name='LongDescription']">
        <xsl:copy>
            <xsl:apply-templates select="Name"/>
            <Value>
                <xsl:value-of select="concat(preceding-sibling::ConfigurationAttribute[Name='ConfigurationModel']/Value, ', ',
                                             preceding-sibling::ConfigurationAttribute[Name='Handing']/Value, ', ',
                                             preceding-sibling::ConfigurationAttribute[Name='EXWidth']/Value, '&quot;', ' X ',
                                             preceding-sibling::ConfigurationAttribute[Name='EXHeight']/Value, '&quot;')"/>
            </Value>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

XSLT Fiddle Demo

Python

import lxml.etree as et

# LOAD XML AND XSL
doc = et.parse('/path/to/Input.xml')
xsl = et.parse('/path/to/XSLT_Script.xsl')

# CONFIGURE TRANSFORMER
transform = et.XSLT(xsl)    

# RUN TRANSFORMATION
result = transform(doc)

# PRINT RESULT
print(result)  

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

【讨论】:

  • 这非常有效。非常感谢您的帮助!
  • 太棒了!很高兴能提供帮助。
猜你喜欢
  • 2015-11-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多