我有一个XSLT,我在其中创建(来自输入数据)中间变量,如下所示(硬编码示例,但本质上是动态的):
<xsl:variable name="variableX">
<ValidCode CodePart="CP1" Code="C1"/>
<ValidCode CodePart="CP2" Code="C2"/>
<ValidCode CodePart="CP1" Code="C3"/>
<ValidCode CodePart="CP2" Code="C4"/>
<ValidCode CodePart="CP2" Code="C5"/>
</xsl:variable>
我希望循环使用不同的CodePart值。在XSLT 2.0中,它很简单:
<xsl:for-each select="distinct-values($variableX/ValidCode/@CodePart)">...</xsl:for-each>
但是在XSLT 1.0中如何做到这一点呢? 请注意,我不能使用密钥,因为它是动态确定的变量而不是输入文件的一部分。
我的输入文件包含所有可能代码部分的列表,如下所示:
<root>
<CodePart><value>CP1</value></CodePart>
<CodePart><value>CP2</value></CodePart>
<CodePart><value>CP3</value></CodePart>
</root>
所以我考虑循环//CodePart/value
,确保初学者的独特性。但后来我需要一些包含条件
“值出现在所有$ variableX / ValidCode / @ CodePart值的节点集中”
并使用类似
的内容<xsl:for-each select="//CodePart[..condition..]/value">...</xsl:for-each>
我正在寻找一种简单形式的Xpath表达式吗? 或者另一种方法更可取?
答案 0 :(得分:4)
请注意,我不能使用密钥,因为它是动态确定的变量 而不是输入文件的一部分。
这根本不是真的。
这是一个使用密钥的简短而简单且最有效的解决方案(顺便说一下,根本不使用任何条件指令或xsl:for-each
)):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="kCodeByPart" match="ValidCode" use="@CodePart"/>
<xsl:variable name="variableX">
<ValidCode CodePart="CP1" Code="C1"/>
<ValidCode CodePart="CP2" Code="C2"/>
<ValidCode CodePart="CP1" Code="C3"/>
<ValidCode CodePart="CP2" Code="C4"/>
<ValidCode CodePart="CP2" Code="C5"/>
</xsl:variable>
<xsl:variable name="vCodes" select="ext:node-set($variableX)/*"/>
<xsl:template match="/">
<xsl:apply-templates select=
"$vCodes[generate-id()
=
generate-id(key('kCodeByPart', @CodePart)[1])
]"/>
</xsl:template>
<xsl:template match="ValidCode">
Code Part: <xsl:value-of select="@CodePart"/>
Codes: <xsl:apply-templates select=
"key('kCodeByPart', @CodePart)/@Code"/>
</xsl:template>
<xsl:template match="@Code">
<xsl:value-of select="concat(., ' ')"/>
</xsl:template>
</xsl:stylesheet>
当对任何XML文档(未使用)应用此转换时,会生成所需的正确结果:
Code Part: CP1
Codes: C1 C3
Code Part: CP2
Codes: C2 C4 C5
<强>解释强>:
如 W3C XSLT 1.0 Recommendation :
中所述"The key can be used to retrieve a key from a document other than the document containing the context node."
在提供的解决方案中,我们甚至不使用这种可能性 - 上下文节点和检索到的密钥都来自同一文档 - $vCodes
中包含的临时树。
同样,指定一个密钥可用于多个文档:
“样式表使用。为每个文档声明一组键 xsl:key元素。“
用简单的话来说:索引是在当前文档(包含上下文(当前)节点的文档)上执行的。
答案 1 :(得分:3)
在XPath 1.0中,您可以通过查找具有相同值的前一个兄弟来模拟distinct-values
函数。
我已将您的第一个文档简化为此,因此可以更容易地复制(甚至不使用XSLT):
<?xml version="1.0"?>
<root>
<ValidCode CodePart="CP1" Code="C1"/>
<ValidCode CodePart="CP2" Code="C2"/>
<ValidCode CodePart="CP1" Code="C3"/>
<ValidCode CodePart="CP2" Code="C4"/>
<ValidCode CodePart="CP2" Code="C5"/>
<ValidCode CodePart="CP3" Code="C5"/>
</root>
然后,以下XPath表达式将仅选择一个CodePart
属性,其中包含文档中出现的每个值:
//ValidCode/@CodePart[not(../preceding-sibling::ValidCode/@CodePart = .)]
因此,要选择的CodePart
属性的条件是<ValidCode>
元素没有前一个兄弟,其CodePart
属性与当前检查的属性(选定)具有相同的值CodePart
属性。
答案 2 :(得分:2)
我认为你不能直接使用XPath - 但是你应该只能检查这样的有效值:
<xsl:for-each select="//CodePart/value">
<xsl:variable name="CodePart" select="."/>
<xsl:if test="$variableX/ValidCode[@CodePart=$CodePart]">
. . .
</xsl:if>
</xsl:for-each>
或更简单(感谢评论):
<xsl:for-each select="//CodePart/value">
<xsl:if test="$variableX/ValidCode[@CodePart=current()]">
. . .
</xsl:if>
</xsl:for-each>
如果$variableX
不是节点集,而是XML片段,则需要将其转换为节点集 - 这与使用Microsoft处理器的实现有关:
<xsl:for-each select="//CodePart/value" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:if test="msxsl:node-set($variableX)/ValidCode[@CodePart=current()]">
. . .
</xsl:if>
</xsl:for-each>
答案 3 :(得分:2)
解决此问题的一种方法(没有密钥)是对preceding-sibling
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:variable name="variableX">
<ValidCode CodePart="CP1" Code="C1"/>
<ValidCode CodePart="CP2" Code="C2"/>
<ValidCode CodePart="CP1" Code="C3"/>
<ValidCode CodePart="CP2" Code="C4"/>
<ValidCode CodePart="CP2" Code="C5"/>
</xsl:variable>
<xsl:variable name="refvarX" select="msxsl:node-set($variableX)" />
<xsl:template match="/">
<root>
<xsl:for-each select="$refvarX/ValidCode">
<xsl:sort select="./@CodePart" data-type="text" />
<xsl:if test="not(./@CodePart = preceding-sibling::ValidCode/@CodePart)">
<cp>
<xsl:value-of select="./@CodePart" />
</cp>
</xsl:if>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>
答案 4 :(得分:0)
使用node-set()
函数,例如使用saxon:
<xsl:template match="/">
<xsl:apply-templates select="root/CodePart/value[
. = saxon:node-set($variableX)/ValidCode/@CodePart]/>
</xsl:template>
<xsl:template match="value">
<!-- template for distinct value -->
</xsl:template>