2010-10-29 53 views
3

我有自动生成HTML的沃兹做愚蠢的事情是这样的:合并元素序列在XSLT

<p>Hey it's <em>italic</em><em>italic</em>!</p> 

而且我想混搭下来到:

<p>Hey it's <em>italicitalic</em>!</p> 

我的第一个尝试沿着这些线...

<xsl:template match="em/preceding::em"> 
    <xsl:value-of select="$OPEN_EM"/> 
    <xsl:apply-templates/> 
</xsl:template> 

<xsl:template match="em/following::em"> 
    <xsl:apply-templates/> 
    <xsl:value-of select="$CLOSE_EM"/> 
</xsl:template> 

但显然XSLT规范在其祖母的好意禁止使用sta ndard XPath precedingfollowing模板匹配器中的轴。 (并且无论如何,这需要进行一些调整来处理连续的三个ems。)

任何解决方案都比忘记在XSLT中执行此操作而只是在$ LANGUAGE_OF_CHOICE中运行replace('</em><em>', '')最终结果?粗糙的要求:如果两个<em>之间由任何东西(空格,文本,标签)分开,并且不需要合并它们,则应该至少生成有效的XML,如果连续有三个或更多个<em> 。处理嵌套在ems中的标签(包括其他ems)不是必需的。

(呵呵,我见过how to merge element using xslt?,这是相似但不相同的XSLT 2遗憾的是不能选择和提出的解决方案看起来可怕复杂。)

+0

好问题,+1。查看我的答案,获取简短而完整的XSLT 1.0解决方案。 :) – 2010-10-29 04:44:41

回答

2

这也是像分组邻道:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:template match="node()|@*"> 
     <xsl:copy> 
      <xsl:apply-templates select="node()[1]|@*"/> 
     </xsl:copy> 
     <xsl:apply-templates select="following-sibling::node()[1]"/> 
    </xsl:template> 
    <xsl:template match="em"> 
     <em> 
      <xsl:call-template name="merge"/> 
     </em> 
     <xsl:apply-templates 
      select="following-sibling::node()[not(self::em)][1]"/> 
    </xsl:template> 
    <xsl:template match="node()" mode="merge"/> 
    <xsl:template match="em" name="merge" mode="merge" > 
     <xsl:apply-templates select="node()[1]"/> 
     <xsl:apply-templates select="following-sibling::node()[1]" 
          mode="merge"/> 
    </xsl:template> 
</xsl:stylesheet> 

输出:

<p>Hey it's <em>italicitalic</em>!</p> 

注意:精细graneid穿越身份规则(复制一切,节点的节点); em规则(始终是第一个,因为该过程是逐节点的),打包并调用merge模板,将模板应用于下一个同级而不是em; 规则在merge模式下(也称为merge),适用于第一个子模板(这种情况下它只是一个文本节点,但允许嵌套元素),然后在merge模式下转到下一个兄弟模板; “break”规则,匹配任何不是em(因为名字测试优先于节点类型测试)停止进程。

+0

@Alejandro:这是一个非常短的解决方案,但很难理解。我需要一个调试器来查看发生了什么。这对于最后两个模板的组合尤其如此。 – 2010-10-29 18:30:20

+0

@Dimitre:你觉得呢?对于分组邻接者来说,这又是一个相同的模式。复制一切,先在组中匹配,进入开放模式(处理所有兄弟,停止出组),不处理组中的下一个兄弟。 – 2010-10-29 18:43:15

+0

@Alejandro:在你的回答中解释这一点很好。另外,最后一个模板覆盖其以前的模板并不太明显。此外,模式的名称令人困惑(至少对我来说)。一个更好的名字是“merge”或“merge-em”。为了使处理过程非常容易理解,我将以这种方式重写空模板:'' – 2010-10-29 20:57:54

2

这种转变

当在下面的XML文档施加(基于所提供的文档,但具有三个相邻 em元素)
<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 

<xsl:key name="kFollowing" 
    match="em[preceding-sibling::node()[1][self::em]]" 
    use="generate-id(preceding-sibling::node()[not(self::em)][1])"/> 

<xsl:template match="node()|@*"> 
    <xsl:copy> 
    <xsl:apply-templates select="node()|@*"/> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match= 
    "em[following-sibling::node()[1][self::em] 
     and 
     not(preceding-sibling::node()[1][self::em]) 
     ]"> 
    <em> 
    <xsl:apply-templates select= 
    "node() 
    | 
     key('kFollowing', 
      generate-id(preceding-sibling::node()[1]) 
     )/node()"/> 
    </em> 
</xsl:template> 
<xsl:template match= 
"em[preceding-sibling::node()[1][self::em]]"/> 
</xsl:stylesheet> 

<p>Hey it's <em>italic1</em><em>italic2</em><em>italic3</em>!</p> 

产生想要的,正确的结果

<p>Hey it's <em>italic1italic2italic3</em>!</p> 

请注意

  1. 使用该身份规则的每一个节点复制为是。

  2. 使用密钥为了方便地指定下列相邻的em元素。

  3. 身份的首要仅变换em元件具有相邻em元件。

  4. 此转换合并任意数量的相邻em元素