将文件名信息收集到 XML 节点中



我有几个XML文件,如下所示:

文件 : 1.xml

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <config>
       <info>
           <info1>val1</info1>
           <info2>val2</info2>
       </info>
       <info>
           <info1>val3</info1>
           <info2>val4</info2>
       </info>
   </config>

文件 : 2.xml

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <config>
       <info>
           <info1>val5</info1>
           <info2>val6</info2>
       </info>
       <info>
           <info1>val7</info1>
           <info2>val8</info2>
       </info>
   </config>

文件: 3.xml

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <config>
       <info>
           <info1>val9</info1>
           <info2>val10</info2>
       </info>
       <info>
           <info1>val11</info1>
           <info2>val12</info2>
       </info>
   </config>

使用 XSLT2.0(撒克逊),我想合并它们并添加到每个节点:

<info3>XXX</info3>

还有

<file>filename.xml</file>

文件名.xml 是从中复制信息的文件。

输出应如下所示:

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <config>
       <info>
           <info1>val1</info1>
           <info2>val2</info2>
           <info3>XXX</info3>
           <file>1.xml</file>
       </info>
       <info>
           <info1>val3</info1>
           <info2>val4</info2>
           <info3>XXX</info3>
           <file>1.xml</file>
       </info>
       <info>
           <info1>val5</info1>
           <info2>val6</info2>
           <info3>XXX</info3>
           <file>2.xml</file>
       </info>
       <info>
           <info1>val7</info1>
           <info2>val8</info2>
           <info3>XXX</info3>
           <file>2.xml</file>
       </info>
       <info>
           <info1>val9</info1>
           <info2>val10</info2>
           <info3>XXX</info3>
           <file>3.xml</file>
       </info>
       <info>
           <info1>val11</info1>
           <info2>val12</info2>
           <info3>XXX</info3>
           <file>3.xml</file>
       </info>
   </config>

到目前为止,我已经能够通过创建一个列出我要合并的文件的 XML 文件来合并文件(merge.xml):

<mergeData newRoot="config">
   <filelist>
       <fileItem>1.xml</fileItem>
       <fileItem>2.xml</fileItem>
       <fileItem>3.xml</fileItem>
   </filelist>
</mergeData>

使用以下 XSL (merge.xsl):

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0" exclude-result-prefixes="#all">
<xsl:param  name="new">
   <info>XXX</info>
</xsl:param>
<xsl:template match="/">
   <xsl:element name="{mergeData/@newRoot}">
      <xsl:apply-templates select="mergeData/fileList/fileItem"/>
   </xsl:element>
</xsl:template>
<xsl:template match="fileItem">
     <xsl:apply-templates select="document(translate(., '', '/'))/config/*"/>
</xsl:template>
<xsl:template match="config/*">
   <xsl:copy>
       <xsl:copy-of select="node()"/>
       <xsl:copy-of select="$new"/>
   </xsl:copy>
   <file><xsl:value-of select="tokenize(document-uri(.), '/')[last()]"/></file>
 </xsl:template>

我应该如何修改 XSL 以同时将文件名放入每个信息中。

实际上,您唯一要做的就是将file移动到xsl:copy内。

示例(还有其他几个小模组):

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0" exclude-result-prefixes="#all">
    <xsl:output indent="yes"/>
    <xsl:param  name="new">
        <info3>XXX</info3>
    </xsl:param>
    <xsl:template match="/">
        <xsl:element name="{mergeData/@newRoot}">
            <xsl:apply-templates select="mergeData/filelist/fileItem"/>
        </xsl:element>
    </xsl:template>
    <xsl:template match="fileItem">
        <xsl:apply-templates select="document(translate(., '', '/'))/config/*"/>
    </xsl:template>
    <xsl:template match="config/*">
        <xsl:copy>
            <xsl:copy-of select="node(),$new"/>
            <file><xsl:value-of select="tokenize(document-uri(/), '/')[last()]"/></file>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

您也可以使用 collection() 而不是创建单独的 mergeData.xml 文件来执行此操作:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>
    <xsl:param name="newRoot" select="'config'"/>
    <xsl:param name="new">
        <info3>XXX</info3>
    </xsl:param>
    <xsl:template match="/">
        <xsl:element name="{$newRoot}">
            <xsl:apply-templates select="collection('file:///C:/some/path?select=[0-9]*.xml')/*/info"/>
        </xsl:element>
    </xsl:template>
    <xsl:template match="info">
        <xsl:copy>
            <xsl:copy-of select="@*|node(),$new"/>
            <file><xsl:value-of select="tokenize(document-uri(/),'/')[last()]"/></file>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

由于您使用的是 Saxon,另一种选择是将saxon:discard-document()mergeData.xml输入一起使用。如果您在 mergeData.xml 中列出了大量文件,这有助于减少内存消耗。(它确实需要 Saxon PE 或 EE 或允许扩展功能的旧版本的 Saxon。

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>
  <xsl:param name="new">
    <info3>XXX</info3>
  </xsl:param>
  <xsl:template match="/mergeData">
    <xsl:element name="{@newRoot}">
      <xsl:apply-templates select="filelist/fileItem"/>
    </xsl:element>
  </xsl:template>
  <xsl:template match="fileItem">
    <xsl:apply-templates select="document(.)/saxon:discard-document(.)/*/*" xmlns:saxon="http://saxon.sf.net/"/>          
  </xsl:template>
  <xsl:template match="info">
    <xsl:copy>
      <xsl:copy-of select="@*|node(),$new"/>
      <file><xsl:value-of select="tokenize(document-uri(/),'/')[last()]"/></file>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

以下 XSLT 将生成所需的结果:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xsl:stylesheet 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" >
  <xsl:output method="xml" encoding="UTF-8"/>
  <xsl:param  name="new">
    <info>XXX</info>
  </xsl:param>
  <xsl:template match="/mergeData">
    <config>
    <xsl:for-each select="filelist/fileItem">
      <xsl:variable name="filename" select="text()"/>
      <xsl:for-each select="document($filename)/config/info">
        <info>
          <xsl:copy-of select="./*"/>
          <xsl:element name="info{count(*)+1}">
            <xsl:value-of select="$new"/>
          </xsl:element>
          <file><xsl:value-of select="$filename"/></file>
        </info>
      </xsl:for-each>
    </xsl:for-each>
    </config>
  </xsl:template>
</xsl:stylesheet>

笔记:

  • 这已经适用于 XSLT 1.0,因此我更改了 XSLT 声明。
  • 由于这种方法是自上而下的预定义子结构,因此它不再使用输入文件的属性newRoot
  • 答案不会提取输入文件的基本名称,而是使用合并配置中提供的完整路径。您可能希望还原此简化。当然,使用 tokenize 会将其推回 XSLT 2.0 或扩展函数。

最新更新