【问题标题】:Insert XSLT node-set B in another node-set A at detected position inside text node of node-set A将 XSLT 节点集 B 插入另一个节点集 A 中节点集 A 的文本节点内检测到的位置
【发布时间】:2011-10-18 19:29:06
【问题描述】:

我正在使用 XSLT 将内容管理系统的复杂 XML 输出转换为 XHTML。我使用<xsl:apply-templates/> 来获取XML 输入所描述的任何内容的XHTML 片段。 XML 输入带有一个非常复杂的结构,可以描述许多不同的情况,由几个 XSLT 模板元素处理。而且这种结构在未来可能会经常发生变化。

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

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

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

接下来在变量 sn-p 中有第二个 XHTML 片段:

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

要求将 $sn-p 中的节点集插入到 $container 值末尾的任何可选包含的句点之前。除非必须将两个变量中的 XHTML 片段保存为片段,否则这不会有问题。因此,不能对任一变量的字符串值进行操作。

是否有机会在 XSLT 中实现该要求,同时又不失&lt;xsl:apply-templates/&gt; 在检索 $container 中的 XHTML 片段时的强大功能和灵活性?

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

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

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

【问题讨论】:

    标签: xslt


    【解决方案1】:

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

    说明:标识规则被递归命名模板覆盖,以查找字符串中的最后一个'.'


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

    【讨论】:

      猜你喜欢
      • 2021-02-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多