假设我有一个像这样的xml.document(不完整):
<root>
<elem id="1"/>
<elem id="2"/>
<elem id="3"/>
</root>
我通过doc-function访问它,如下所示:
<xsl:variable name="curDoc" select="doc(iri-to-uri("abc.xml"))"/>
现在我想将应用样式表的文档中的每个元素与该abc.xml中的每个元素进行比较。
我应用样式表的文档如下所示:
<node>
<somenode id="1"/>
<somenode id="2"/>
<somenode id="3"/>
</node>
现在两个文件中的元素实际上都没有按顺序排列!
还有另外一种方法可以做到这一点:
<xsl:template match="somenode">
<xsl:variable name="curElem" select="."/>
<xsl:for-each select="$curDoc/root/elem">
<xsl:if test="@id = $curElem[@id]">
<!-- do something -->
<xsl:if>
</xsl:for-each>
</xsl:template>
我读了很多你应该避免的 - 并且只是使用模板机制,但是我怎么能在没有for-each的情况下这样做(使用XSLT 2.0)?
答案 0 :(得分:2)
将条件移至<for-each>
选择。
<xsl:template match="somenode">
<xsl:variable name="currentElementId" select="./@id"/>
<xsl:for-each select="$curDoc/root/elem[@id = $currentElementId]">
<!-- do something -->
</xsl:for-each>
</xsl:template>
此解决方案仍然使用循环。这是正确的,因为节点列表可能为空,或者您可以使用具有相同elem
属性值的多个id
元素。
在XML中,属性id
不一定是唯一的,它取决于文档定义(DTD,XSD,...)。
大多数函数使用列表中的第一个节点,因此它应该在没有for-each的情况下工作。实际上。
<xsl:template match="somenode">
<xsl:variable name="currentElementId" select="./@id"/>
<xsl:variable name="targetNode" select="$curDoc/root/elem[@id = $currentElementId]">
<xsl:if test="$targetNode">
<!-- do something with $targetNode -->
</xsl:if>
</xsl:template>
答案 1 :(得分:2)
xsl:for-each本质上不是邪恶的。只是过度使用xsl:for-each(和xsl:if)通常是程序员的一个症状,他们没有掌握语言中更强大的结构。
查看代码,实际上是在进行连接,在XSLT中进行连接的最佳方法是使用密钥。所以我会把它写成
<xsl:key name="id" match="elem" use="@id"/>
<xsl:template match="somenode">
<xsl:for-each select="key('id', @id, $curdoc)">
<!-- do something -->
</xsl:for-each>
</xsl:template>
您可以使用xsl:apply-templates替换xsl:for-each,也许是在特定模式下,但除非有某些原因,否则我不会这样做。期望有人可能希望有一天能代替你做某事&#34;做某事&#34;用&#34;做些不同的事情&#34;。
答案 2 :(得分:0)
以下是一个小例子,您可以在不使用<xsl:for-each>
指令的情况下完成所需操作:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="vDoc">
<root>
<elem id="1"/>
<elem id="2"/>
<elem id="3"/>
</root>
</xsl:variable>
<xsl:template match="somenode">
<xsl:apply-templates select="$vDoc/*/elem[@id eq current()/@id]">
<xsl:with-param name="porigElem" select="."/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="elem">
<xsl:param name="porigElem" as="element()"/>
Match:
Original: <xsl:sequence select="$porigElem"/>
Matching: <xsl:sequence select="."/>
</xsl:template>
</xsl:stylesheet>
在提供的源XML文档上应用此转换(重新排序,以匹配问题的描述):
<node>
<somenode id="3"/>
<somenode id="1"/>
<somenode id="2"/>
</node>
产生了想要的正确结果:
Match:
Original: <somenode id="3"/>
Matching: <elem id="3"/>
Match:
Original: <somenode id="1"/>
Matching: <elem id="1"/>
Match:
Original: <somenode id="2"/>
Matching: <elem id="2"/>
当然,如果想按照凯博士的回答中的建议使用密钥,必须定义密钥,然后模板的应用将如下所示:
<xsl:apply-templates select="key('kelemById', @id, $vDoc)">
<xsl:with-param name="porigElem" select="."/>
</xsl:apply-templates>
现在完全转型是:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="kelemById" match="elem" use="@id"/>
<xsl:variable name="vDoc">
<root>
<elem id="1"/>
<elem id="2"/>
<elem id="3"/>
</root>
</xsl:variable>
<xsl:template match="somenode">
<xsl:apply-templates select="key('kelemById', @id, $vDoc)">
<xsl:with-param name="porigElem" select="."/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="elem">
<xsl:param name="porigElem" as="element()"/>
Match:
Original: <xsl:sequence select="$porigElem"/>
Matching: <xsl:sequence select="."/>
</xsl:template>
</xsl:stylesheet>
当此转换应用于同一源XML文档时,会产生相同的想要和正确的结果。
答案 3 :(得分:-1)
我不确定这是否更好,但这应该有效:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="somenode">
<xsl:variable name="curElem" select="."/>
<xsl:apply-templates select="$curDoc" />
</xsl:template>
<xsl:template match="root/elem">
<xsl:if test="@id = $curElem[@id]">
test
</xsl:if>
</xsl:template>