呼叫命名模板下一个兄弟

问题描述:

我有下面的XML:呼叫命名模板下一个兄弟

<Text> 
     <p id="258">Step.</p> 
     <p id="1123">Step info.</p> 
     <p id="258">Step.</p> 
     <p id="1123">Step info.</p> 
     <p id="258">Step.</p> 
     <p id="1123">Step info:</p> 
     <p id="1123">- Comment.</p> 
     <p id="1123">- Comment.</p> 
     <p id="1123">- Comment.</p> 
    </Text> 

我必须把它变成一个DocBook <orderedlist>

<orderedlist> 
     <listitem> 
      <para>Step.</para> 
      <para> 
       <emphasis>Step info.</emphasis> 
      </para> 
     </listitem> 
     <listitem> 
      <para>Step.</para> 
      <para> 
       <emphasis>Step info.</emphasis> 
      </para> 
     </listitem> 
     <listitem> 
      <para>Step.</para> 
      <para> 
       <emphasis>Step info:</emphasis> 
      </para> 
      <para> 
       <emphasis>- Comment:</emphasis> 
      </para> 
      <para> 
       <emphasis>- Comment:</emphasis> 
      </para> 
      <para> 
       <emphasis>- Comment:</emphasis> 
      </para> 
     </listitem> 
    </orderedlist> 

我第一次把所有<p id="258">元素融入<listitem><para>

<xsl:template match="AIT:p[@id='258'][1]"> 
    <orderedlist> 
     <xsl:for-each select="../AIT:p[@id='258']"> 
      <xsl:call-template name="stepNoLine"/> 
     </xsl:for-each> 
    </orderedlist> 
</xsl:template> 

<xsl:template name="stepNoLine"> 
    <listitem> 
     <para> 
      <xsl:apply-templates select="*|node()"/> 
     </para> 
    </listitem> 
</xsl:template> 

而我删除所有非第一个元素:

<xsl:template match="AIT:p[@id='258'][position() > 1]"/> 

到目前为止好:

<orderedlist> 
     <listitem> 
      <para>Step.</para> 
     </listitem> 
     <listitem> 
      <para>Step.</para> 
     </listitem> 
     <listitem> 
      <para>Step.</para> 
     </listitem> 
    </orderedlist> 

但现在我不知道如何利用<p id="1123">元素的照顾。两个<p id="258">之间的所有<p id="1123">必须是第一个<p id="258">的兄弟姐妹,以及<listitem>的子女。还是那句话:

<listitem> 
     <para>Step.</para> 
     <para> 
      <emphasis>Step info.</emphasis> 
     </para> 
    </listitem> 

我微不足道的尝试失败可耻羞辱:

<xsl:template name="stepNoLine"> 
    <listitem> 
     <para> 
      <xsl:apply-templates select="*|node()"/> 
     </para> 
     <xsl:if test="following-sibling::AIT:p/@id='1123'"> 
      <xsl:call-template name="stepInfo"/> 
     </xsl:if> 
    </listitem> 
</xsl:template> 

<xsl:template name="stepInfo"> 
    <para> 
     <emphasis> 
      <xsl:apply-templates select="*|node()"/> 
     </emphasis> 
    </para> 
</xsl:template> 

我得到的是这样的:

<orderedlist> 
     <listitem> 
      <para>Step.</para> 
      <para> 
       <emphasis>Step.</emphasis> 
      </para> 
     </listitem> 
     <listitem> 
      <para>Step.</para> 
      <para> 
       <emphasis>Step.</emphasis> 
      </para> 
     </listitem> 
     <listitem> 
      <para>Step.</para> 
      <para> 
       <emphasis>Step.</emphasis> 
      </para> 
     </listitem> 
    </orderedlist> 

换句话说,每个<p id="258">元素被复制两次。我以为<xsl:if>是当前节点的下一个兄弟,但我显然是错了。

其他尝试(如使用xsl:for-each而不是xsl:if)以同样悲惨的方式失败。

有人可以请指出我在正确的方向吗?

+1

您可以使用XSLT,2.0? –

+0

我使用的是msxsl,但根据Microsoft仅XSLT 1.0。 我看到Saxon-HE 9.8实现了XSLt 3.0 - 会这样吗? –

XSLT 2.0或3.0,您可以使用for-each-group group-starting-with

<xsl:template match="Text"> 
    <orderedlist> 
     <xsl:for-each-group select="*" group-starting-with="p[@id = 258]"> 
      <listitem> 
       <xsl:apply-templates select="current-group()"/> 
      </listitem> 
     </xsl:for-each-group> 
    </orderedlist> 
</xsl:template> 

<xsl:template match="Text/p[@id = 258]"> 
    <para> 
     <xsl:apply-templates/> 
    </para> 
</xsl:template> 

<xsl:template match="Text/*[not(self::p[@id = 258])]"> 
    <para> 
     <emphasis> 
      <xsl:apply-templates/> 
     </emphasis> 
    </para> 
</xsl:template> 
+0

谢谢你,模板工作打算(虽然我不知道我知道他们是如何做到这一点)。 –

如今的正确答案这种几乎所有的问题将是“使用XSLT 2.0”,但在环境中唯一的XSLT 1.0可用在1.0中解决问题也很有用。

考虑下面的输入,同构的例子所示,但具有不同的字符数据,使其更容易地看到发生了什么:

<Text> 
    <p id="258">First step.</p> 
    <p id="1123">Step info for step 1.</p> 
    <p id="258">Step two.</p> 
    <p id="1123">Step info for second step.</p> 
    <p id="258">Step three.</p> 
    <p id="1123">Step info for third step:</p> 
    <p id="1123">- Comment on step 3.</p> 
    <p id="1123">- Comment 2 on step 3.</p> 
    <p id="1123">- Comment 3 on step 3.</p> 
</Text> 

有了该输入,这是一个比较容易看到,(一)你的草稿样式表(假设我已经从你的描述中正确地重构了它)确实为每个步骤设置了一个listItem(尽管它使用了稍微圆满的方法),但是(b)它没有模板匹配Text元素或任何p元素与id="1123",这意味着默认模板会激发并且我们得到它们的转储在第一个258段的模板输出之后的1123段的字符数据。

<orderedlist> 
<listitem> 
    <para>First step.</para> 
    <para><emphasis>First step.</emphasis></para> 
</listitem> 
<listitem> 
    <para>Step two.</para> 
    <para><emphasis>Step two.</emphasis></para> 
</listitem> 
<listitem> 
    <para>Step three.</para> 
    <para><emphasis>Step three.</emphasis></para> 
</listitem> 
</orderedlist> 
     Step info for step 1. 

     Step info for second step. 

     Step info for third step: 
     - Comment on step 3. 
     - Comment 2 on step 3. 
     - Comment 3 on step 3. 

代码眼前的问题是,stepNoLine电话stepinfo没有做任何事情来改变上下文节点,所以模板应用有处理258款,而不是下面的段落1123的子女。

你正在处理的输入具有的嵌入式元素序列中的大量信息;你的拉式样式表忽略了这些信息,并试图从零开始重新创建它。您的样式表会更简单,如果你让他们通过输入为指导,在什么通常是在XSLT编程为“推”风格着称会做的更好的工作。只为这应该产生listItem元素,即p元素与id="258"儿童应用模板:

在下面的XSLT样式表,为Text模板调用XSL。 1123段不被该指令处理。

258段元素的模板反过来创建列表项的所有内容:首先是258段,然后是所有后续p元素的序列id="1123"。 (我们只对第一个模板应用模板,但第一个模板只处理整个序列。)

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
       version="1.0"> 

    <xsl:template match="Text"> 
    <xsl:element name="orderedList"> 
     <xsl:apply-templates select="p[@id='258']"/> 
    </xsl:element> 
    </xsl:template> 

    <xsl:template match="p[@id='258']"> 
    <xsl:element name="listitem"> 
     <xsl:element name="para"> 
     <xsl:apply-templates/> 
     </xsl:element> 
     <xsl:apply-templates select="following-sibling::*[1]/self::p[@id='1123']"/> 
    </xsl:element> 
    </xsl:template> 

    <xsl:template match="p[@id='1123']"> 
    <xsl:element name="para"> 
     <xsl:apply-templates/> 
    </xsl:element> 
    <xsl:apply-templates select="following-sibling::*[1]/self::p[@id='1123']"/> 
    </xsl:template> 

</xsl:stylesheet> 

N.B.在模板258个elemetns我们不要试图模板应用到所有适当的兄弟元素与id="1123"。我们可以,而且一切都将是简单的,如果我们有一个便捷的方式说,XPath 1.0中,“所有紧随p元素与id="1123"直到但不包括第一个pid="258",或父元素结束” ,并且确定地知道我们说得对。说“接下一个兄弟姐妹,当且仅当它是一个pid="1123",并且比那个元素的模板做同样的事情更容易。当我们到达与id="1123"其紧随其后的兄弟不是 。id="1123",或者有没有以下的兄弟姐妹,递归停止

从修改的输入,这将产生作为输出:

<?xml version="1.0" encoding="UTF-8"?> 
<orderedList> 
    <listitem> 
    <para>First step.</para> 
    <para>Step info for step 1.</para> 
    </listitem> 
    <listitem> 
    <para>Step two.</para> 
    <para>Step info for second step.</para> 
    </listitem> 
    <listitem> 
    <para>Step three.</para> 
    <para>Step info for third step:</para> 
    <para>- Comment on step 3.</para> 
    <para>- Comment 2 on step 3.</para> 
    <para>- Comment 3 on step 3.</para> 
    </listitem> 
</orderedList> 

我相信这是需要什么样的(如果你想换行1123。段落'内容emph,应该很容易看到在哪里做t帽子。)

+0

@ CMS-McQ,我可以说什么,我向你的掌握鞠躬。非常感谢你。 –

<xsl:template match="Text"> 
    <xsl:element name="orderedlist"> 
     <xsl:apply-templates select="p[@id='258']"/> 
    </xsl:element> 
</xsl:template> 

<xsl:template match="p"> 
    <xsl:choose> 
     <xsl:when test="@id='258'"> 
      <xsl:element name="listitem"> 
       <xsl:element name="para"><xsl:apply-templates /></xsl:element> 
       <xsl:apply-templates select="following-sibling::p[1][@id = '1123']"/> 
      </xsl:element> 
     </xsl:when> 
     <xsl:otherwise> 
      <xsl:element name="para"> 
       <xsl:element name="emphasis"> 
        <xsl:apply-templates /> 
       </xsl:element> 
      </xsl:element> 
      <xsl:apply-templates select="following-sibling::p[1][@id = '1123']"/> 
     </xsl:otherwise> 
    </xsl:choose> 
</xsl:template> 

+0

全部,谢谢你的回复。但是,我意识到我的问题并不十分精确。 从我的例子中所有的元素都意味着是同的孩子,但一个元素也可以包含不同的列表或嵌套的列表。 变成并不总是如在所有贡献的中一样。 对不起,不清楚。所有的答案都能解决问题,但我没有很好地说明问题。 我同意XSLT 2.0并不总是答案,但我正在寻找与组相邻的(h/t @Martin)。 –