我正在使用XSLT将内容管理系统的复杂XML输出转换为XHTML。我正在使用<xsl:apply-templates/>
来获取XML输入描述的XHTML片段。 XML输入带有一个非常复杂的结构,可以描述许多不同的情况,由几个XSLT模板元素处理。而且这种结构将来可能会经常发生变化。
以前,该转换的结果片段直接发送到XSLT输出。现在需求已经改变,我需要捕获结果,偶尔修改它以在片段的值的某个位置插入一些其他格式良好的XHTML片段。
为了演示,请考虑<xsl:apply-templates/>
创建了一些在变量容器中捕获的不透明XHTML片段。
<xsl:variable name="container">
<xsl:apply-templates />
</xsl:variable>
接下来,在变量代码段中有第二个XHTML片段:
<xsl:variable name="snippet">
<xsl:call-template name="get-snippet" />
</xsl:variable>
要求说要在 $ snippet 中的节点设置插入 $ container 的值结尾之前的任何可选包含句点之前插入。除非必须将两个变量中的XHTML片段保持为片段,否则这不成问题。因此,无法对任一变量的字符串值进行操作。
是否有机会在XSLT中实现该要求而不会失去<xsl:apply-templates/>
在 $ container 中检索XHTML片段的能力和灵活性?
BTW:我已经知道使用以下方法访问$container
中的最后一个文本节点
node-set($container)//child::text[last()]
但我错过了在该文本节点的中间插入一些东西,我认为XSLT无法为我想要做的事情提供适当的支持。
答案 0 :(得分:2)
<强>予。 XSLT 1.0解决方案:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common"
exclude-result-prefixes="ext">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="vrtfContainer">
<html>
<p>Hello, world.</p>
<p> This is <b>just</b> a <i>demo.</i></p>
<p> Of some text</p>
</html>
</xsl:variable>
<xsl:variable name="vContainer" select=
"ext:node-set($vrtfContainer)/*"/>
<xsl:variable name="vrtfSnippet">
<p>Snippet</p>
</xsl:variable>
<xsl:variable name="vSnippet" select=
"ext:node-set($vrtfSnippet)/*"/>
<xsl:variable name="vText" select=
"($vContainer//text()[contains(.,'.')])[last()]"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:apply-templates select="$vContainer"/>
</xsl:template>
<xsl:template match="text()">
<xsl:choose>
<xsl:when test="not(generate-id() = generate-id($vText))">
<xsl:value-of select="."/>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="insertSnippet">
<xsl:with-param name="pText" select="$vText"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="insertSnippet">
<xsl:param name="pText"/>
<xsl:copy-of select="substring-before($pText, '.')"/>
<xsl:variable name="vTail" select=
"substring-after($pText, '.')"/>
<xsl:choose>
<xsl:when test="not(contains(substring($vTail,2), '.'))">
<xsl:copy-of select="$vSnippet"/>
<xsl:value-of select="concat('.', $vTail)"/>
</xsl:when>
<xsl:otherwise>
<xsl:text>.</xsl:text>
<xsl:call-template name="insertSnippet">
<xsl:with-param name="pText"
select="substring-after($pText, '.')"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
当应用此转换时(对任何XML文档 - 未使用),将生成所需的正确结果:
<html>
<p>Hello, world.</p>
<p> This is <b>just</b> a <i>demo
<p>Snippet</p>.</i></p>
<p> Of some text</p>
</html>
解释:身份规则由递归命名模板覆盖,以查找字符串中的最后一个'.'
。
<强> II。 XSLT 2.0解决方案:
<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="vContainer">
<html>
<p>Hello, world.</p>
<p> This is <b>just</b> a <i>demo.</i></p>
<p> Of some text</p>
</html>
</xsl:variable>
<xsl:variable name="vSnippet">
<p>Snippet</p>
</xsl:variable>
<xsl:variable name="vText" select=
"($vContainer//text()[contains(.,'.')])[last()]"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:apply-templates select="$vContainer/*"/>
</xsl:template>
<xsl:template match="text()[. is $vText]">
<xsl:variable name="vInd" select=
"index-of(string-to-codepoints(.), string-to-codepoints('.'))[last()]"/>
<xsl:sequence select="substring(., 1, $vInd -1)"/>
<xsl:sequence select="$vSnippet/*"/>
<xsl:sequence select="substring(., $vInd)"></xsl:sequence>
</xsl:template>
</xsl:stylesheet>
执行此XSLT 2.0转换后,再次生成所需的正确结果:
<html>
<p>Hello, world.</p>
<p> This is <b>just</b> a <i>demo
<p>Snippet</p>.</i></p>
<p> Of some text</p>
</html>
解释:使用标准XPath 2.0函数string-to-codepoints()
,index-of()
,substring()
和运算符is
。