首页 文章

将XSLT节点集B插入到节点集A的文本节点内检测到的位置处的另一个节点集A.

提问于
浏览
1

我'm using XSLT to transform complex XML output of a content management system into XHTML. I'使用 <xsl:apply-templates/> 来获取XML输入描述的XHTML片段 . XML输入带有一个非常复杂的结构,可以描述许多不同的情况,由几个XSLT模板元素处理 . 而且这种结构将来可能会经常发生变化 .

以前,该转换的结果片段直接发送到XSLT输出 . 现在需求已经改变,我需要捕获结果,偶尔修改它以在片段的值的某个位置插入一些其他格式良好的XHTML片段 .

为了演示,考虑 <xsl:apply-templates/> 创建了一些在变量 container 中捕获的不透明XHTML片段 .

<xsl:variable name="container">
  <xsl:apply-templates />
</xsl:variable>

接下来在变量 snippet 中有第二个XHTML片段:

<xsl:variable name="snippet">
  <xsl:call-template name="get-snippet" />
</xsl:variable>

要求说 $snippet 中的节点集在 $container 的值结尾处的任何可选包含句点之前插入 . 这个ain 't problematic unless XHTML fragments in both variables have to be kept as fragments. Thus one can' t对任一变量的字符串值进行操作 .

有没有机会在XSLT中实现该要求而不会失去 <xsl:apply-templates/>$container 中检索XHTML片段的能力和灵活性?

BTW:我已经知道如何使用以下方法访问 $container 中的最后一个文本节点:

node-set($container)//child::text[last()]

但我错过了在该文本节点的中间插入一些东西,我认为XSLT无法为我想做的事情提供适当的支持 .

1 回答

  • 2

    I. XSLT 1.0 solution

    <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>
    

    When this transformation is applied (to any XML document -- not used), the wanted, correct result is produced

    <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>
    

    Explanation :身份规则由递归命名模板覆盖,以查找字符串中的最后一个 '.' .


    II. XSLT 2.0 solution

    <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>
    

    When this XSLT 2.0 transformation is performed, again the wanted correct result is produced

    <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>
    

    Explanation :使用标准XPath 2.0函数 string-to-codepoints()index-of()substring() 和operator is .

相关问题