我对XSLT很新,因此我想知道检查属性存在的最佳做法是什么。我的XML看起来像这样:
<root>
<languages>
<lang id="EN">English<lang>
<lang id="FR">French<lang>
<lang id="DE">German</lang>
</languages>
<items>
<item lang="EN">test 1</item>
<item>test 2</item>
<item lang="FR">item 3</item>
</items>
</root>
请注意,'item'元素的'lang'属性是可选的。
现在我想使用-loop遍历项目,同时检查它是否有“lang”属性。如果是,我想使用ID获取整个字符串(例如EN - &gt;'英语')。如果没有设置属性,我希望它写“没有语言设置”或类似的东西。
现在我使用以下代码,但是我在质疑自己是否无法以更有效的方式完成。
<xsl:for-each select="//root/items/item">
<xsl:variable name="cur_lang" select="@lang" /> <!-- first I store the attr lang in a variable -->
<xsl:choose>
<xsl:when test="@lang"> <!-- then i test if the attr exists -->
<xsl:value-of select="//root/languages/lang[@id=$cur_lang]" /> <!-- if so, parse the element value -->
</xsl:when>
<xsl:otherwise>
No language set <!-- else -->
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
有任何建议/提示吗?
答案 0 :(得分:7)
使用密钥可能更有效。您可以使用模板外的顶级元素
来定义键<xsl:key name="langByCode" match="lang" use="@id" />
然后在循环中你可以简单地说
<xsl:when test="@lang"> <!-- then i test if the attr exists -->
<xsl:value-of select="key('langByCode', @lang)" />
</xsl:when>
但总的来说,对整个事情采用更自然的XSLT方法是使用模板匹配而不是for-each
和if
:
<xsl:template match="item[@lang]">
<xsl:value-of select="key('langByCode', @lang)" />
</xsl:template>
<xsl:template match="item">
<xsl:text>No language set</xsl:text>
</xsl:template>
使用这些模板,您可以<xsl:apply-templates select="/root/items/item" />
,它会自动为每个项目选择适当的模板。规则是它将使用最具体的模板,因此item[@lang]
用于那些具有lang
属性的项目,而item
用于那些不具有<xsl:value-of select="
substring(
concat('No language set', key('langByCode', @lang)),
1 + (15 * boolean(@lang))
)" />
属性的项目。
第三种可能性是我在SO上学到的一个小技巧,将整个if / else检查放入单个XPath表达式
boolean(@lang)
这里的诀窍是,如果lang属性存在,1
被视为数字0
,如果不存在则lang="EN"
。"No language setEnglish"
如果有一个"English"
,那么我们构造一个字符串"No language set"
,然后从第16个字符开始获取子字符串,即"No color specified"
。如果有 no lang属性,我们构造字符串<xsl:value-of select="substring(
concat('No color specified', @color),
1 + (18 * boolean(@color))
)" />
(因为空节点集的字符串值是空字符串)并从第一个字符开始获取子字符串(即整个字符串)字符串)。
您可以使用与其他属性相同的技巧,例如假设我们有一个可选的颜色属性,想要说{{1}}如果不存在,你可以用
来做{{1}}
答案 1 :(得分:0)
如果您能够使用XSLT 3.0,另一种选择是map(另一个有用的链接:map)。
XML输入(已修复为格式良好)
<root>
<languages>
<lang id="EN">English</lang>
<lang id="FR">French</lang>
<lang id="DE">German</lang>
</languages>
<items>
<item lang="EN">test 1</item>
<item>test 2</item>
<item lang="FR">item 3</item>
</items>
</root>
XSLT 3.0
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
xmlns:xs="http://www.w3.org/2001/XMLSchema" extension-element-prefixes="xs map">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="lang-map" as="map(xs:string, xs:string)"
select="map:new(
for $lang in /*/languages/lang
return
map{$lang/@id := $lang/string()}
)"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="languages"/>
<xsl:template match="item[@lang and map:contains($lang-map,@lang)]">
<item><xsl:value-of select="$lang-map(current()/@lang)"/></item>
</xsl:template>
<xsl:template match="item">
<item>No language found.</item>
</xsl:template>
</xsl:stylesheet>
<强>输出强>
<root>
<items>
<item>English</item>
<item>No language found.</item>
<item>French</item>
</items>
</root>