【问题标题】:xml bulk file editor increment by numbersxml批量文件编辑器按数字递增
【发布时间】:2017-08-10 00:57:33
【问题描述】:

我试图在我的所有 xml 文件中以增量方式为 TEXT_VALUE 字段的值添加前缀,只有带有 ID="Example" 的名为“TRANSL”的标签。 目前我正在手动进行,但由于我有数千个,我认为我应该以编程方式进行。

这是初始版本:

<TRANSL ID="Example">
    <TRANSCIPT>
        <REF_TEXT TEXT_ID="a680" TXT_TM="a24">
            <TEXT_VALUE>this is an example</TEXT_VALUE>
        </REF_TEXT>
    </TRANSCIPT>
    <TRANSCIPT>
        <REF_TEXT TEXT_ID="a681" TXT_TM="a25">
            <TEXT_VALUE>another example</TEXT_VALUE>
        </REF_TEXT>
    </TRANSCIPT>
    <TRANSCIPT>
        <REF_TEXT TEXT_ID="a682" TXT_TM="a26">
            <TEXT_VALUE>third example</TEXT_VALUE>
        </REF_TEXT>
    </TRANSCIPT>
</TRANS>

这是经过编辑的版本:

<TRANSL ID="Example">
    <TRANSCIPT>
        <REF_TEXT TEXT_ID="a680" TXT_TM="a24">
            <TEXT_VALUE>1-this is an example</TEXT_VALUE>
        </REF_TEXT>
    </TRANSCIPT>
    <TRANSCIPT>
        <REF_TEXT TEXT_ID="a681" TXT_TM="a25">
            <TEXT_VALUE>2-another example</TEXT_VALUE>
        </REF_TEXT>
    </TRANSCIPT>
    <TRANSCIPT>
        <REF_TEXT TEXT_ID="a682" TXT_TM="a26">
            <TEXT_VALUE>3-third example</TEXT_VALUE>
        </REF_TEXT>
    </TRANSCIPT>
</TRANS>

如何以编程方式进行?有没有专业的 xml 编辑器?如果没有,我该如何在 python、powershell、perl、notepad ++ 或任何其他中做到这一点?

这是我在 python 中作为记事本 ++ 插件的脚本:

def increment_replace(match):
    return "<TEXT_VALUE>{}".format(str(int(match.group(1))+1))

editor.rereplace(r'\<TEXT_VALUE\>', increment_replace)

但它不起作用......

【问题讨论】:

    标签: python regex xml xml-parsing auto-increment


    【解决方案1】:

    要使用count(preceding-sibling::*) 将变体XSLT 脚本添加到@zx485,请考虑使用lxml 的以下Python 解决方案。作为信息,XSLT 是一种用于转换 XML 文件的专用语言,它可以成为将初始 XML 文件操作为最终使用格式的便捷工具。

    由于 Python 是一种通用语言,您可以利用其 os 文件系统模块和第三方模块 lxml(具有 XPath 1.0 和 XSLT 1.0 功能的完全兼容 W3C 库)迭代地创建所需的输出.

    XSLT (另存为 .xsl 文件以在 Python 中解析)

    <xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output version="1.0" encoding="UTF-8" indent="yes" method="xml"/>
    <xsl:strip-space elements="*"/>
    
      <!-- Identity Transform -->
      <xsl:template match="@*|node()">
        <xsl:copy>
          <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
      </xsl:template>
    
      <!-- Add Incremenet Number to Text -->
      <xsl:template match="TEXT_VALUE[ancestor::TRANSL/@ID='Example']">
        <xsl:copy>      
          <xsl:value-of select="concat(count(ancestor::TRANSCIPT/preceding-sibling::TRANSCIPT)+1, '-', text())"/>
        </xsl:copy>
      </xsl:template>
    
    </xsl:transform>
    

    Python

    import os
    import lxml.etree as et
    
    # CHANGE DIRECTORY
    os.chdir('/path/to/raw/XML/files')
    
    # LOAD XSLT SCRIPT AND INITIALIZE TRANSFORMER
    xslt = et.parse('/path/to/XSLT_Script.xsl')
    transform = et.XSLT(xslt)
    
    for file in os.listdir():
       if file.endswith('.xml'):
    
          # LOAD SOURCE XML
          dom = et.parse(file)
    
          # TRANSFORM TO NEW TREE
          newdom = transform(dom)
    
          # SAVE TO FILE (SAME NAME WITH _new SUFFIX)
          with open(file.replace('.xml', '_new.xml'), 'wb') as f:
              f.write(newdom)
    

    【讨论】:

      【解决方案2】:

      要获取&lt;TEXT_VALUE&gt; 元素的当前计数/position(),您可以参考父&lt;TRANSCIPT&gt; 元素的计数/position()

      要将此计数传递给后续模板I used the solution from this SO answer 并将其方法合并到身份模板中,现在传递一个包含一些值的num 参数。 num 参数在所有 &lt;TRANSCIPT&gt; 元素之上的 &lt;for-each&gt; 循环中生成,并向下传递 &lt;apply-templates&gt; 层次结构以在 TEXT_VALUE 模板中使用(其他任何地方都将被忽略)。

      <?xml version="1.0" encoding="UTF-8"?>
      <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
      
        <!-- modified identity template -->
        <xsl:template match="node()|@*">
          <xsl:param name="num" />
          <xsl:copy>  
            <xsl:apply-templates select="node()|@*">
              <xsl:with-param name="num" select="$num"/>
            </xsl:apply-templates>
          </xsl:copy>
        </xsl:template>
      
        <xsl:template match="TRANSL">
          <xsl:copy>
            <xsl:apply-templates select="@*" />
            <xsl:for-each select="TRANSCIPT">
              <xsl:copy>
              <xsl:apply-templates>
                <xsl:with-param name="num" select="position()" />
              </xsl:apply-templates>
              </xsl:copy>
            </xsl:for-each>    
          </xsl:copy>
        </xsl:template>
      
        <xsl:template match="TEXT_VALUE[../../../@ID='Example']">   <!-- added after extension of question -->
          <xsl:param name="num" />
          <xsl:element name="TEXT_VALUE">
            <xsl:value-of select="concat($num,'-',text())" />
          </xsl:element>        
        </xsl:template>
      
      </xsl:stylesheet>
      

      编辑:
      在评论中扩展了要求后,我向TEXT_VALUE 模板添加了一个谓词,修改了匹配规则以仅选择具有@ID 属性值为“示例”的TEXT_VALUE 元素。

      【讨论】:

      • 我应该如何运行它?我是这个环境的新手......我如何运行它来完成我所有文件中的替换工作?
      • @cplus:您可以使用任何 XSLT-1.0 处理器来完成这项工作。哪一个取决于您的操作系统。在任何 Linux 变体上,您都可以使用例如xsltproc 通常是默认安装的一部分。但也有许多其他适用于 Windows 和 Mac 的。独立于操作系统的是Saxon,它是用 Java 编写的,也支持 XSLT-2.0。
      • 是的。使用 for i in *.xml; do xsltproc xsltfilename.xslt $i; done;xsltfilename 表示我的答案的 XSLT 代码。
      • xsltproc's -o 参数将转换保存到文件。
      • @cplus:@Parfait 是对的。所以使用for i in *.xml; do xsltproc xsltfilename.xslt -o $i.res $i; done;之类的东西将所有.xml文件转换为.xml.res文件。
      猜你喜欢
      • 1970-01-01
      • 2022-11-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多