使用 Python 和正则表达式编辑本地 XML 文件

Editing local XML file using Python and Regular expression

我是 python 的新手,正在尝试修改我本地系统中存在的一些 xml 配置文件。

输入:我有一个包含以下内容的 xml 文件(比如 Test.xml)。

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <JavaHost xmlns="SomeInfo/v1.1">
        <Domain>
           <MessageProcessor>
              <!-- This comment should not be removed and all formating should be untouched -->
              <SocketTimeout>500</SocketTimeout>
           </MessageProcessor>
            <!-- This comment should not be removed and all formating should be untouched -->
           <Composer>
                <SocketTimeout>5000</SocketTimeout>
                <Enabled>true</Enabled>
           </Composer> 
       </Domain>
    </JavaHost>

我想要实现的目标: 我想实现以下两件事:

第 1 部分: 我想将 SocketTimeout 标签(仅在 composer 标签下)的值修改为 60,并且还想添加这样的注释(例如更改此值以减少 SocketTimeout)。 因此文件 Test.xml 应该如下所示:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <JavaHost xmlns="SomeInfo/v1.1">
       <MessageProcessor>
          <!-- This comment should not be removed and all formating should be untouched -->
          <SocketTimeout>500</SocketTimeout>
       </MessageProcessor>
        <!-- This comment should not be removed and all formating should be untouched -->
       <Composer>
       <!-- Changed this value to reduce SocketTimeout -->
            <SocketTimeout>60</SocketTimeout>
            <Enabled>true</Enabled>
       </Composer>
   </Domain>
</JavaHost>

第 2 部分: 在文件 Test.xml 中,我想在域标签下添加一个新标签,如下所示:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <JavaHost xmlns="SomeInfo/v1.1">
       <MessageProcessor>
          <!-- This comment should not be removed and all formating should be untouched -->
          <SocketTimeout>500</SocketTimeout>
       </MessageProcessor>
       <!-- comment should not be removed and all formatting should be untouched -->
       <Composer>
       <!-- Changed this value to reduce SocketTimeout -->
            <SocketTimeout>60</SocketTimeout>
            <Enabled>true</Enabled>
       </Composer>
       <New_tag>
       <!-- New Tag -->
            <Enabled>true</Enabled>
       </New_tag>
   </Domain>
</JavaHost>

这就是我想要的:)

我尝试过的:

为了完成这个任务,我考虑了以下选项:

Minidom/ElementTree/lxml 删除文件中的注释并更改文件的格式。

正则表达式:不删除评论,也不影响格式。 因此,我选择了正则表达式,下面是我开始的,但没有用:(

import os, re
# set the working directory 
os.chdir('C:\Users\Dell\Desktop\')

# open the source file and read it
fh = open('C:\Users\Dell\Desktop\Test.xml', 'r')
subject = fh.read()
fh.close()

pattern = re.compile(r"\[<Composer>\].*?\[/<Composer>\]")
#Replace
result = pattern.sub(lambda match: match.group(0).replace('<SocketTimeout>500</SocketTimeout>','<SocketTimeout>60</SocketTimeout>') ,subject)

# write the file
f_out = open('C:\Users\Dell\Desktop\Test.xml', 'w')
f_out.write(result)
f_out.close()

任何实现我想要的东西或纠正错误的想法都将非常感激。 虽然我是 python 的新手,但我会尽力处理这些建议。

不完全您想要的,但已经很接近了。一方面,避免 xml、html 和类似处理 的正则表达式,如瘟疫 。同时,如果您在使用 lxml.

等产品时偶尔会发现 'challenges',请不要感到惊讶。

我想,这次我发现了一个错误。

from lxml import etree
tree = etree.parse('shivam.xml')
element_to_change = tree.xpath('.//Composer/SocketTimeout')[0]
print(element_to_change)
element_to_change.text='60'
comment_will_follow_this = tree.xpath('.//Composer')[0]
print(comment_will_follow_this)
comment = etree.Comment('This did not work')
comment_will_follow_this.append(comment)

comment = etree.Comment('Changed this value to reduce SocketTimeout')
element_to_change.addprevious(comment)

tree.write('see_it.xml', pretty_print=True)
  • 我使用 xpath 找到要更改的元素,以及文件中接收评论的位置。
  • append 方法应该将注释或其他元素作为子元素添加到给定元素。但是,我发现在这种情况下 'This did not work' 注释被添加为前面的元素注释。
  • 然而,我确实发现 addprevious 能够在所需位置添加评论,美中不足的是它无法在评论和下一个评论之间放置 end-line xml 元素。

这是生成的文件。顺便说一句,你会注意到原来的评论是完整的。

<JavaHost>
    <Domain>
       <MessageProcessor>
          <!-- This comment should not be removed and all formating should be untouched -->
          <SocketTimeout>500</SocketTimeout>
       </MessageProcessor>
        <!-- This comment should not be removed and all formating should be untouched -->
       <Composer>
            <!--Changed this value to reduce SocketTimeout--><SocketTimeout>60</SocketTimeout>
            <Enabled>true</Enabled>
       <!--This did not work--></Composer> 
   </Domain>
</JavaHost>

由于您在同一句话中使用了 modifyXML,请考虑 XSLT, the special-purpose language designed to modify XML files. Python's lxml can run XSLT 1.0 scripts as well as external processors 或其他语言 Python 可以在命令行调用。所以,XSLT 是可移植的!更重要的是,Python 可以将参数传递给 XSLT,以防 50 需要动态调整 - 非常类似于其他 special-purpose 语言中的参数,SQL,其中 Python 具有许多 API。

具体来说,XSLT 维护了 <xsl:comment> 命令并且可以将节点追加或重写到树中。此外,正如评论、链接和希望网络搜索推荐的那样,在 non-natural 语言的 X|HTML 文档上使用正则表达式是高度 ill-adivsed。因此,DOM 像 Python 的 etree、lxml、minidom 这样的库是首选,当然 XSLT 也符合 W3C 标准。

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

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

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

    <xsl:template match="Domain">
     <xsl:copy>       
       <xsl:apply-templates select="*|@*|comment()"/>
       <New_tag>
         <xsl:comment>New Tag</xsl:comment>
         <Enabled>true</Enabled>
       </New_tag>
     </xsl:copy>
    </xsl:template>

    <xsl:template match="Composer">
     <xsl:copy>
       <xsl:comment>Changed this value to reduce SocketTimeout</xsl:comment>
       <SocketTimeout>50</SocketTimeout>
       <xsl:apply-templates select="Enabled"/>
     </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

Python

import lxml.etree as et

# LOAD XML AND XSLT
dom = et.parse('Input.xml') 
xslt = et.parse('XSLT_Script.xsl')

# TRANSFORM SOURCE
transform = et.XSLT(xslt)
newdom = transform(dom)

# OUTPUT TO CONSOLE
print(newdom)

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

输出

<JavaHost>
  <Domain>
    <MessageProcessor>
      <!-- This comment should not be removed and all formating should be untouched -->
      <SocketTimeout>500</SocketTimeout>
    </MessageProcessor>
    <!-- This comment should not be removed and all formating should be untouched -->
    <Composer>
      <!--Changed this value to reduce SocketTimeout-->
      <SocketTimeout>50</SocketTimeout>
      <Enabled>true</Enabled>
    </Composer>
    <New_tag>
      <!--New Tag-->
      <Enabled>true</Enabled>
    </New_tag>
  </Domain>
</JavaHost>