I have the following 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>
I must turn it into a 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>
I first turn all <p id="258">
elements into <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>
And I delete all non-first elements:
<xsl:template match="AIT:p[@id='258'][position() > 1]"/>
So far so good:
<orderedlist>
<listitem>
<para>Step.</para>
</listitem>
<listitem>
<para>Step.</para>
</listitem>
<listitem>
<para>Step.</para>
</listitem>
</orderedlist>
But now I do not know how to take care of <p id="1123">
elements. All <p id="1123">
between two <p id="258">
must be siblings of the first <p id="258">
, and children of <listitem>
. Again:
<listitem>
<para>Step.</para>
<para>
<emphasis>Step info.</emphasis>
</para>
</listitem>
My puny attempt disgracefully fails in dishonor:
<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>
I get something like:
<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>
In other words, each <p id="258">
element is copied twice. I thought the <xsl:if>
made the next sibling the current node, but I was evidently mistaken.
Other attempts (like using a xsl:for-each
instead of xsl:if
) failed in equally miserable ways.
Can someone please point me in the right direction?
XSLT 2.0 or 3.0 you can use 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>