快速问题:是否有一种方法可以通过使用变量来增加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>
</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>
</xsl:text>
</xsl:for-each>
</xsl:element>
<xsl:text>
</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
值的键—特别是当您期望大量行时。试一试:
<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的答案,并考虑使用关键函数作为你的解决方案,因为它非常有用