使用XSLT计算不同的项并解析逗号分隔的值
假设我有如下XML:使用XSLT计算不同的项并解析逗号分隔的值,xslt,parsing,Xslt,Parsing,假设我有如下XML: <child_metadata> <metadata> <attributes> <metadata_valuelist value="[SampleItem3]"/> </attributes> </metadata> <metadata> <attributes>
<child_metadata>
<metadata>
<attributes>
<metadata_valuelist value="[SampleItem3]"/>
</attributes>
</metadata>
<metadata>
<attributes>
<metadata_valuelist value="[SampleItem1]"/>
</attributes>
</metadata>
<metadata>
<attributes>
<metadata_valuelist value="[SampleItem1, SampleItem2]"/>
</attributes>
</metadata>
</child_metadata>
...
<metadata_valuelist>
<value>SampleItem1</value>
<value>SampleItem2</value>
</metadata_valuelist>
...
我要做的是计算元数据值列表中不同值的数量。有以下不同的值:SampleItem1、SampleItem2和SampleItem3。所以,我想得到一个值3。(虽然SampleItem1出现了两次,但我只计算了一次。)
如何在XSLT中实现这一点
我意识到这里有两个问题:第一,在列表中分隔逗号分隔的值,第二,计算唯一值的数量。然而,我不确定我是否能将这两个问题的解决方案结合起来,这就是为什么我要把它作为一个问题来问。你可能想分两个阶段来考虑;首先,进行分解这些值属性的转换,然后对它们进行计数就相当简单了
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="@value">
<xsl:call-template name="breakdown">
<xsl:with-param name="itemlist" select="substring-before(substring-after(.,'['),']')" />
</xsl:call-template>
</xsl:template>
<xsl:template name="breakdown">
<xsl:param name="itemlist" />
<xsl:choose>
<xsl:when test="contains($itemlist,',')">
<xsl:element name="value">
<xsl:value-of select="normalize-space(substring-before($itemlist,','))" />
</xsl:element>
<xsl:call-template name="breakdown">
<xsl:with-param name="itemlist" select="substring-after($itemlist,',')" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:element name="value">
<xsl:value-of select="normalize-space($itemlist)" />
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
除了底部的“一网打尽”模板外,它还会以您提供的格式提取任何值属性,并将它们分解为单独的元素(作为“元数据\ U valuelist”元素的子元素),如下所示:
<child_metadata>
<metadata>
<attributes>
<metadata_valuelist value="[SampleItem3]"/>
</attributes>
</metadata>
<metadata>
<attributes>
<metadata_valuelist value="[SampleItem1]"/>
</attributes>
</metadata>
<metadata>
<attributes>
<metadata_valuelist value="[SampleItem1, SampleItem2]"/>
</attributes>
</metadata>
</child_metadata>
...
<metadata_valuelist>
<value>SampleItem1</value>
<value>SampleItem2</value>
</metadata_valuelist>
...
。。。
样本项目1
样本项目2
...
在将“[”和“]”传递到“细分”模板之前,顶部附近的“选择前的子字符串/选择后的子字符串”会将“[”和“]”去掉。此模板将检查它的“itemlist”参数中是否有逗号,如果有,它会将前面的文本作为“value”元素的内容吐出,然后使用列表的其余部分递归调用自己。如果参数中没有逗号,它只将参数的整个内容作为“value”元素输出
然后运行以下命令:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" />
<xsl:key name="itemvalue" match="value" use="text()" />
<xsl:template match="/">
<xsl:value-of select="count(//value[generate-id(.) = generate-id(key('itemvalue',.)[1])])" />
</xsl:template>
</xsl:stylesheet>
在第一次转换得到的XML上,它只会输出一个值作为文本输出,告诉您有多少不同的值
编辑:我可能应该指出,这个解决方案对您的输入做出了一些假设:
- 文档中其他任何位置都没有名为“value”的属性;如果有,您可以修改@value匹配来特别挑选这些
- 文档中其他任何位置都没有名为“value”的元素;当第一个变换创建它们时,第二个变换将无法区分两者。如果有,您可以用尚未使用的元素名称替换这两行
- @value属性的内容始终以“[”开头,以“]”结尾,列表中没有“]”字符;如果存在,则“substring before”函数将删除第一个']'之后的所有内容,而不仅仅是结尾的']'
- 要计数的项目名称中没有逗号,例如[SampleItem1,“Sample2,3”]。如果有,“‘样本2’和‘样本3’”将被视为单独的项目
- 另一种没有扩展的方式:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:variable name="all-value" select="/*/*/*/*/@value"/>
<xsl:template match="/">
<xsl:variable name="count">
<xsl:apply-templates select="$all-value"/>
</xsl:variable>
<xsl:value-of select="string-length($count)"/>
</xsl:template>
<xsl:template match="@value" name="value">
<xsl:param name="meta" select="translate(.,'[] ','')"/>
<xsl:choose>
<xsl:when test="contains($meta,',')">
<xsl:call-template name="value">
<xsl:with-param name="meta" select="substring-before($meta,',')"/>
</xsl:call-template>
<xsl:call-template name="value">
<xsl:with-param name="meta" select="substring-after($meta,',')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:if test="count(.|$all-value[contains(translate(.,'[] ','


'),
concat('
',$meta,'
'))][1])=1">
<xsl:value-of select="1"/>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
注意:可能可以使用xsl:key
而不是xsl:variable
编辑:匹配棘手的元数据。此(注意:仅一个)转换:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
>
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kValue" match="value" use="."/>
<xsl:template match="/">
<xsl:variable name="vRTFPass1">
<values>
<xsl:apply-templates/>
</values>
</xsl:variable>
<xsl:variable name="vPass1"
select="msxsl:node-set($vRTFPass1)"/>
<xsl:for-each select="$vPass1">
<xsl:value-of select=
"count(*/value[generate-id()
=
generate-id(key('kValue', .)[1])
]
)
"/>
</xsl:for-each>
</xsl:template>
<xsl:template match="metadata_valuelist">
<xsl:call-template name="tokenize">
<xsl:with-param name="pText" select="translate(@value, '[],', '')"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="tokenize">
<xsl:param name="pText" />
<xsl:choose>
<xsl:when test="not(contains($pText, ' '))">
<value><xsl:value-of select="$pText"/></value>
</xsl:when>
<xsl:otherwise>
<value>
<xsl:value-of select="substring-before($pText, ' ')"/>
</value>
<xsl:call-template name="tokenize">
<xsl:with-param name="pText" select=
"substring-after($pText, ' ')"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
<child_metadata>
<metadata>
<attributes>
<metadata_valuelist value="[SampleItem3]"/>
</attributes>
</metadata>
<metadata>
<attributes>
<metadata_valuelist value="[SampleItem1]"/>
</attributes>
</metadata>
<metadata>
<attributes>
<metadata_valuelist value="[SampleItem1, SampleItem2]"/>
</attributes>
</metadata>
</child_metadata>
3
请注意:因为这是一个XSLT1.0解决方案,所以有必要将第一次传递的结果从臭名昭著的RTF类型转换为常规树。这是使用XSLT 1.0处理器的xxx:node-set()函数完成的——在我的例子中,我使用了msxsl:node-set()。此解决方案在这种特殊情况下会产生预期的结果。但是,如果给定以下值:
[SampleItem3]
,[SampleItem1SampleItem2,SampleItem2]
,[SampleItem1,SampleItem1SampleItem2]
,则会生成错误答案:3。正确的答案是:4.这是一个很好的解决方案;次要的一点,但是你不需要顶部有一个
元素吗?@Dimitre:你说得对!我编辑答案。另外,我正在寻找一种不必两次导航树的解决方案。@flyn1179:你说得对。为了生成完整的样式表,它需要xsl:output
。但有时我们倾向于将这些转换视为最大样式表的一部分,甚至视为包含或导入。谢谢!请注意我的后续问题: