每次for-each迭代时XSL谓词递增



快速问题:是否有一种方法可以通过使用变量来增加XPATH的谓词,就像在C中遍历数组一样?例如/XPATH/element[i]

我正在尝试使用XSL使用xpath从XML访问数据。XML是数据库的输出,其中父节点是表名,其子节点是列。XSL必须能够将子元素的文本值转换为具有表名元素的列名的属性。

我试图解决的问题是,每个表可以有多个行,这些行作为具有相同名称的兄弟节点输出到XML。任何表中都可能有无限行,因此我尝试使用for-each和表名的XPATH来处理每一行。这是有效的,但是当我尝试将文档函数与XPATH一起使用,并对第一个XPATH进行谓词,然后是下一个XPATH,然后是下一个XPATH时,我不知道如何做到这一点。我只能访问第一个XPATH。我希望有一种方法能够在每次for-each迭代时访问下一个XPATH。是否有任何东西可以增加每个循环,并且谓词和用法指向下一个XPATH?

下面的XML代码是我用来测试的一个示例,它被称为DB.xml:
<?xml version="1.0"?>
<dataset>
 <rtbp>
  <cfmtype>dog</cfmtype>
  <cfmid>1</cfmid>
 </rtbp>
 <rtbp>
  <cfmtype>cat</cfmtype>
  <cfmid>2</cfmid>
 </rtbp>
 <FunctionSet> 
  <FUNCTIONSET__IDENTIFIER>1</FUNCTIONSET__IDENTIFIER>
  <RTBP__CFMID>1</RTBP__CFMID>
 </FunctionSet>
 <FunctionSet> 
  <FUNCTIONSET__IDENTIFIER>2</FUNCTIONSET__IDENTIFIER>
  <RTBP__CFMID>2</RTBP__CFMID>
 </FunctionSet>
</dataset>

下面是我使用的XSL:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>
 <xsl:template match="/">
  <xsl:for-each select="dataset/rtbp">
   <xsl:element name="RTBP">
    <xsl:attribute name="CFMtype">
     <xsl:value-of select="document('DB.xml')/dataset/rtbp[1]/cfmtype" />
    </xsl:attribute>
    <xsl:attribute name="CFMid">
     <xsl:value-of select="document('DB.xml')/dataset/rtbp[1]/cfmid" />
    </xsl:attribute>
    <xsl:text>&#xa;</xsl:text>
    <xsl:for-each select="/dataset/FunctionSet">
     <xsl:element name="FunctionSet">
      <xsl:attribute name="RTBP__CFMid">
       <xsl:value-of select="document('DB.xml')/dataset/FunctionSet[1]/FUNCTIONSET__IDENTIFIER" />
      </xsl:attribute>
      <xsl:attribute name="RTBP_FunctionSet">
       <xsl:value-of select="document('DB.xml')/dataset/FunctionSet[1]/RTBP__CFMID" />
      </xsl:attribute>
     </xsl:element>
     <xsl:text>&#xa;</xsl:text>
    </xsl:for-each>
   </xsl:element>
   <xsl:text>&#xa;</xsl:text>
  </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>

谓词目前设置为1,但我希望它是一个在每个循环中迭代的变量,以便XPATH更改为下一次出现的表名。

预期结果如下:

<?xml version="1.0"?>
<RTBP CFMtype="dog" CFMid="1">
  <FunctionSet RTBP__CFMid="1" RTBP_FunctionSet="1"/>
</RTBP>
<RTBP CFMtype="cat" CFMid="2">
  <FunctionSet RTBP__CFMid="2" RTBP_FunctionSet="2"/>
</RTBP>

你可以看出第二个表(FunctionSet)是第一个表(RTBP)的子表,因此for-each中有for-each。我需要一个方法,将功能集的第一行放入RTBP的第一行,同样也用于第二行。

我是XML, XSL和张贴问题的新手。

目的是从平面XML重新创建分层XML使用DBunit从数据库导出。这种联系是可以实现的由cmfid

您绝对应该使用基于匹配cfmid值的—特别是当您期望大量行时。试一试:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:key name="func" match="FunctionSet" use="RTBP__CFMID" />
<xsl:template match="/">
    <root>
        <xsl:for-each select="dataset/rtbp">
            <RTBP CFMtype="{cfmtype}" CFMid="{cfmid}">
                <xsl:for-each select="key('func', cfmid)">
                    <FunctionSet RTBP__CFMid="{RTBP__CFMID}" RTBP_FunctionSet="{FUNCTIONSET__IDENTIFIER}"/>
                </xsl:for-each>
            </RTBP>
        </xsl:for-each>
    </root>
</xsl:template>
</xsl:stylesheet>

当以上应用于以下测试输入:

<?xml version="1.0"?>
<dataset>
 <rtbp>
  <cfmtype>dog</cfmtype>
  <cfmid>124</cfmid>
 </rtbp>
 <rtbp>
  <cfmtype>cat</cfmtype>
  <cfmid>256</cfmid>
 </rtbp>
 <FunctionSet> 
  <FUNCTIONSET__IDENTIFIER>Canine</FUNCTIONSET__IDENTIFIER>
  <RTBP__CFMID>124</RTBP__CFMID>
 </FunctionSet>
 <FunctionSet> 
  <FUNCTIONSET__IDENTIFIER>Feline</FUNCTIONSET__IDENTIFIER>
  <RTBP__CFMID>256</RTBP__CFMID>
 </FunctionSet>
 <FunctionSet> 
  <FUNCTIONSET__IDENTIFIER>Hound</FUNCTIONSET__IDENTIFIER>
  <RTBP__CFMID>124</RTBP__CFMID>
 </FunctionSet>
</dataset>

结果为:

<?xml version="1.0" encoding="utf-8"?>
<root>
  <RTBP CFMtype="dog" CFMid="124">
    <FunctionSet RTBP__CFMid="124" RTBP_FunctionSet="Canine"/>
    <FunctionSet RTBP__CFMid="124" RTBP_FunctionSet="Hound"/>
  </RTBP>
  <RTBP CFMtype="cat" CFMid="256">
    <FunctionSet RTBP__CFMid="256" RTBP_FunctionSet="Feline"/>
  </RTBP>
</root>

请注意,您请求的输出格式不必要地在父级和子级中重复cfmid值。

我想你要找的是(在问题更新后更新):

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
        <xsl:output indent="yes"/>
        <xsl:template match="node()|@*">
            <xsl:copy>
                <xsl:apply-templates select="node()|@*"/>
            </xsl:copy>
        </xsl:template>
        <xsl:template match="rtbp">
            <xsl:copy>
                <xsl:for-each select="*">
                    <xsl:attribute name="{local-name()}" select="."/>
                </xsl:for-each>
                <xsl:apply-templates 
                             select="//FunctionSet[RTBP__CFMID = current()/cfmid]"
                     mode="insertFunctionSet"/>
            </xsl:copy>
        </xsl:template>
        <xsl:template match="FunctionSet"/>
        <xsl:template match="FunctionSet" mode="insertFunctionSet">
            <xsl:copy>
                <xsl:for-each select="*">
                    <xsl:attribute name="{local-name()}" select="."/>
                </xsl:for-each>
            </xsl:copy>
        </xsl:template>
    </xsl:stylesheet>

这里的想法是在rtbp元素的上下文中以不同的方式处理元素FunctionSet

当您递归地遍历整个树时,它不应该是输出的一部分(这是<xsl:template match="FunctionSet"/>的目标)。

但是它应该在rtbp元素内部处理,因此我们此时以特定模式将模板应用于相关的FunctionSet。这就是<xsl:template match="FunctionSet" mode="insertFunctionSet">...</xsl:template>

的目标。

输入:

<?xml version="1.0"?>
<dataset>
 <rtbp>
  <cfmtype>dog</cfmtype>
  <cfmid>1</cfmid>
 </rtbp>
 <rtbp>
  <cfmtype>cat</cfmtype>
  <cfmid>2</cfmid>
 </rtbp>
 <FunctionSet> 
  <FUNCTIONSET__IDENTIFIER>1</FUNCTIONSET__IDENTIFIER>
  <RTBP__CFMID>1</RTBP__CFMID>
 </FunctionSet>
 <FunctionSet> 
  <FUNCTIONSET__IDENTIFIER>2</FUNCTIONSET__IDENTIFIER>
  <RTBP__CFMID>2</RTBP__CFMID>
 </FunctionSet>
</dataset>

结果是:

<?xml version="1.0" encoding="UTF-8"?>
<dataset>
   <rtbp cfmtype="dog" cfmid="1">
      <FunctionSet FUNCTIONSET__IDENTIFIER="1" RTBP__CFMID="1"/>
   </rtbp>
   <rtbp cfmtype="cat" cfmid="2">
      <FunctionSet FUNCTIONSET__IDENTIFIER="2" RTBP__CFMID="2"/>
   </rtbp>
</dataset>

对于那些在我发布这个问题时和我一样知之甚少的人,以及那些希望找到相同信息的人,这里是我对这个问题的解决方案。对于"你能增加一个变量吗"这个快速问题的简短回答。不!但是,您可以使用以下代码段设置变量并移动位置:

<xsl:for-each select="/dataset/rtbp">
   <xsl:variable name="i" select="position()" />
 </xsl:for-each> 

此代码段循环遍历源XML中的rtbp表,每次循环都将位置移动一个位置。这将创建一个对象,您可以在XPath中使用该对象来测试每次出现具有相同URI路径的XPath的条件。例如:

<xsl:for-each select="/dataset/rtbp">
    <xsl:variable name="i" select="position()" />
    <xsl:if test="/dataset/FunctionSet[$i]/cfmid = /dataset/rtbp[$n]/cfmid">
       <!--code if condition is true-->
 </xsl:for-each> 

[$variable name]是将XPath指向元素名称出现的方式。因此,当i = 1时,它查找XPath中第一次出现的元素名称,然后当i = 2时,它查找XPath中第二次出现的元素名称。

Key函数是在模板中搜索关键条件的好工具。然而,我只能使用一个键函数每个模板。如果您希望进行多条件测试,则必须使用带有多个If语句的select when语句。例如:

这是我的高级代码的一个片段,其中有多个for-each循环,并选择when语句来决定XML元素是否是父元素的子元素,通过它的标识符是父元素的子元素在我的问题中的示例XML。

使用position函数和XPath谓词条目以及带and的select when语句相结合,您可以构建一个复杂的XSL,该XSL可以将数据库的平面XML表列表重新创建为分层XML形式。

Vincent的Key函数答案对于这个问题的小复杂性是有效的,但是这个答案包含了一个关于XPath谓词的答案,所以我认为它更符合这个问题的答案。请参考Vincent的答案,并考虑使用关键函数作为你的解决方案,因为它非常有用

相关内容

  • 没有找到相关文章

最新更新