我有以下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="1123">
之间的所有<p id="258">
必须是第一个<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
)也以同样悲惨的方式失败。
有人可以指出我正确的方向吗?
答案 0 :(得分:1)
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>
答案 1 :(得分:1)
现在几乎所有问题的正确答案都是“使用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>
通过这个输入,可以更容易地看到(a)您的草稿样式表(假设我已经从您的描述中正确地重新构建)确实设法为每个步骤获得一个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编程中通常称为'push'样式。
在以下XSLT样式表中,Text
的模板仅为应生成listItem
元素的子项调用xsl:apply-templates,即p
元素id="258"
。该指令不处理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>
。 (我们仅将模板应用于第一个模板,但第一个模板负责整个序列。)
id="1123"
N.B。在258 elemet的模板中,我们不会尝试使用p
将模板应用于所有适当的兄弟元素。我们可以,而且一切都会更简单,如果我们在XPath 1.0中有一个方便的说法,“id="1123"
所有紧跟p
元素,但不包括第一个id="258"
} p
,或父元素的结尾“,并确切地知道我们已经做对了。说“立即跟随兄弟,当且仅当它是id="1123"
与p
时,并且让该元素的模板做同样的事情时更容易。当我们达到{{1}时id="1123"
p
紧随其后的兄弟不是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>
,或者没有后续兄弟,递归就会停止。
从修改后的输入中,产生输出:
emph
我认为这是必需的。 (如果你想在z-index: 2;
position: relative;
中包装1123段的内容,应该很容易看到在哪里这样做。)
答案 2 :(得分:1)
<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>