我有一堆文档,我正在尝试使用 xslt 设置样式,并且我有一些这样的代码:
<tag1>Something</tag1>
<tag2>Something else</tag2>
<tag3>...</tag3>
但在某些文件中可能只有:
<tag2>Something else</tag2>
<tag3>...</tag3>
当我渲染文档时,我想<h2>Heading</h2>
在 tag1、tag2 或 tag3 之前添加一个“”(以先到者为准)。这可能吗?
就这么简单(没有条件指令,没有xsl:element
,没有starts-with()
,没有preceding-sibling::
轴,没有模式,只有一个覆盖标准身份规则的模板):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[self::tag1 or self::tag2 or self::tag3][1]">
<h2>Heading</h2>
<xsl:call-template name="identity"/>
</xsl:template>
</xsl:stylesheet>
当此转换应用于以下 XML 文档时(将提供的片段包装到单个顶部元素中以成为格式良好的 XML 文档):
<t>
<tag1>Something</tag1>
<tag2>Something else</tag2>
<tag3>...</tag3>
</t>
产生了想要的正确结果:
<t>
<h2>Heading</h2>
<tag1>Something</tag1>
<tag2>Something else</tag2>
<tag3>...</tag3>
</t>
请注意:
如果有很多元素名称(不一定以相同的字符串开头,那么像这样重写最后一个模板的匹配模式会更短更实用:
<xsl:template match=
"*[contains('|tag1|tag2|tag3|', concat('|',name(),'|'))][1]">
<xsl:if test="count(./preceding-sibling::*)=0"><h2>Heading</h2></xsl:if>
这意味着当前节点在文档中的前面有 0 个兄弟元素(即是其父节点的第一个元素)。但正如评论的那样:定位父元素并将 H2 添加到其输出中会产生更好的性能。
总是有一个包含标签,在最坏的情况下它是文档元素本身。
稍微不同的方法:
<xsl:template match="*[tag1|tag2|tag3]">
<xsl:element name="{name()}">
<h2>Heading</h2>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="tag1|tag2|tag3">
<xsl:element name="{name()}"><xsl:apply-templates/></xsl:element>
</xsl:template>
这是我发现对我有用的东西:
<xsl:template match="*[starts-with(name(), 'tag')][1]" priority="1">
<h2>Heading</h2>
<xsl:apply-templates select="." mode="display"/>
</xsl:template>
<xsl:template match="*[starts-with(name(), 'tag')]">
<xsl:apply-templates select="." mode="display"/>
</xsl:template>
<xsl:template match="root/tag1" mode="display">
<p>First tag:
<xsl:apply-templates />
</p>
</xsl:template>
<xsl:template match="root/tag2" mode="display">
<p>Second tag:
<xsl:apply-templates />
</p>
</xsl:template>
<xsl:template match="root/tag3" mode="display">
<p>Third tag:
<xsl:apply-templates />
</p>
</xsl:template>
有点不正统,但效果很好。
这个 XSLT 1.0 样式表...
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" />
<xsl:strip-space elements="*" />
<xsl:template match="@*|node()" name="ident">
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="*[self::tag1|self::tag2|self::tag3]
[not(preceding-sibling::tag1|
preceding-sibling::tag2|
preceding-sibling::tag3)]">
<h2>Heading</h2>
<xsl:call-template name="ident" />
</xsl:template>
</xsl:stylesheet>
...或者等价于这个 XSLT 2.0 一个...
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" />
<xsl:strip-space elements="*" />
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="*[self::tag1|self::tag2|self::tag3]
[not(preceding-sibling::tag1|
preceding-sibling::tag2|
preceding-sibling::tag3)]">
<h2>Heading</h2>
<xsl:next-match />
</xsl:template>
</xsl:stylesheet>
...将转换此输入文档..
<t>
<branch1>
<something-before/>
<tag1>Something</tag1>
<tag2>Something else</tag2>
<tag3>tag3 content</tag3>
<something-after/>
</branch1>
<branch2>
<something-before/>
<tag3>tag3 content</tag3>
<tag2>Something else</tag2>
<something-after/>
</branch2>
</t>
...进入...
<t>
<branch1>
<something-before />
<h2>Heading</h2>
<tag1>Something</tag1>
<tag2>Something else</tag2>
<tag3>tag3 content</tag3>
<something-after />
</branch1>
<branch2>
<something-before />
<h2>Heading</h2>
<tag3>tag3 content</tag3>
<tag2>Something else</tag2>
<something-after />
</branch2>
</t>