快速问题:有没有办法通过使用变量来增加XPATH的谓词,比如迭代C中的数组?例如/ XPATH / element [i]
我正在尝试使用XSL使用XPATHS从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。我需要一个方法,将FunctionSet的第一行放入RTBP的第一行,同样放入第二行。
我是XML,XSL和发布问题的新手。
答案 0 :(得分:2)
目的是从平面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
值。
答案 1 :(得分:1)
我认为你正在寻找类似的东西(在更新后更新):
<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>
这里的想法是在FunctionSet
元素的上下文中以不同的方式处理元素rtbp
。
当你递归遍历整个树时,它不应该是输出的一部分(这是<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>
答案 2 :(得分:-1)
对于那些在我发布这个问题的时候和我一样知识渊博的人,并且希望在这里找到相同信息的人是我对问题的解决方案。简短回答快速问题'你可以增加一个变量'。没有!但您可以设置变量并使用以下代码段移动位置:
<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函数是在模板中搜索关键条件的好工具。但是我每个模板只能使用1个键功能。如果您希望进行多条件测试,则必须使用select when语句,其中多个if语句与eeach other一起使用。例如:
这是来自我的高级代码的片段,其中每个循环都有多个for-each循环,并选择when语句来判断XML元素是否是父元素的子元素,通过其标识符是父元素的子元素。我的问题中的示例XML。
使用position函数和XPath谓词条目结合选择何时使用和可以构建复杂XSL的语句,可以将数据库的平面XML表列表重新创建为分层XML表单。
文森特的关键函数答案适用于这个问题的小复杂性,但这个答案包含了关于XPath谓词的答案,所以我认为它与问题的答案更相关。请查看Vincent的答案并考虑使用Key Functions作为解决方案,因为它非常有用