My goal is to get all text, including text inside elements, between 2 processing instructions using xslt.
Input file is DITA having standard XML-based structure. There are 2 processing instructions I am searching for <?PI start?> and <?PI end?>. I search for text after <?PI start?> and before <?PI end?>. There can be just text or an element that has text in it.
Input
<concept id="testcase" >
<title> Introduction</title>
<conbody>
<p>
<p>text01</p>
<?PI start 1?> text02 <?PI end 1?>
<b> text03 </b>
<?PI start 2?> text04 text05 <?PI end 2?> text06
<?PI start 3?> text07 <?PI end 3?>
</p>
<p>
<?PI start 4?>text11 <?PI end 4?>
<?PI start 5?><b>text12</b><?PI end 5?>
<?PI start 6?> text13<?PI end 6?>
</p>
</conbody>
</concept>
My approaches were:
- match
<?PI start?>, and try to getfollowing-siblinguntil I will get to the<?PI end?>. Problem is there is no break for a loop in xslt as well as there is no way to change the value of variable, so I don't know how to stop.
xsl
<xsl:template match="//processing-instruction('PI')[contains(.,'start')]">
<xsl:variable name='text1' select="following-sibling::text()[preceding::processing-instruction('PI')[1][contains(.,'start')]][following::processing-instruction('PI')[1][contains(., 'end ')]] "/>
<xsl:variable name='text2' select="following-sibling::*[preceding::processing-instruction('PI')[1][contains(.,'start')]][following::processing-instruction('PI')[1][contains(., 'end')]]/text() "/>
<xsl:variable name="text" select="concat($text1,$text2)"/>
<xsl:value-of select="$text"/>
<xsl:copy>
<xsl:apply-templates select="@* | node()" />
</xsl:copy>
</xsl:template>
output
<concept xmlns:ditaarch="http://dita.oasis-open.org/architecture/2005/" id="testcase">
<title> Introduction</title>
<conbody>
<p>
<p>text01</p>
text02 <?PI start 1?> text02 <?PI end 1?>
<b> text03 </b>
text04 text05 <?PI start 2?> text04 text05 <?PI end 2?> text06
text07 <?PI start 3?> text07 <?PI end 3?>
</p>
<p>
text11 text12<?PI start 4?>text11 <?PI end 4?>
text13text12<?PI start 5?>
<b>text12</b><?PI end 5?>
text13<?PI start 6?> text13<?PI end 6?>
</p>
</conbody>
</concept>
- match text or any element that has
preceding-sibling<?PI start?>andfollowing-sibling<?PI end?>.
xsl
<xsl:template match="//processing-instruction('PI')[contains(.,'start')]">
<xsl:for-each select="following-sibling::*">
<xsl:value-of select="./text()"/>
</xsl:for-each>
<xsl:for-each select="following-sibling::text()">
<xsl:value-of select="."/>
</xsl:for-each>
<xsl:copy>
<xsl:apply-templates select="@* | node()" />
</xsl:copy>
</xsl:template>
output
<concept xmlns:ditaarch="http://dita.oasis-open.org/architecture/2005/" id="testcase">
<title> Introduction</title>
<conbody>
<p>
<p>text01</p>
text03 text02
text04 text05 text06
text07
<?PI start 1?> text02 <?PI end 1?>
<b> text03 </b>
text04 text05 text06
text07
<?PI start 2?> text04 text05 <?PI end 2?> text06
text07
<?PI start 3?> text07 <?PI end 3?>
</p>
<p>
text12text11
text13
<?PI start 4?>text11 <?PI end 4?>
text12
text13
<?PI start 5?>
<b>text12</b><?PI end 5?>
text13
<?PI start 6?> text13<?PI end 6?>
</p>
</conbody>
</concept>
Problem is that it matches even the elements that are not between 2 processing instructions. For example text03 from below, as technically it does have preceding-sibling <?PI start?> and following-sibling <?PI end?>:
<?PI start 1?> text02 <?PI end 1?>
<b> text03 </b>
<?PI start 2?> text04 text05 <?PI end 2?>
XSLT version: 1.0
XSLT processor: Saxon-HE
I will appreaciate any input, ideas and sugesstions
<<and>>andfor-each-group group-starting-with/ending-withthere are good tools to handle such cases, in XSLT 1 it gets a bit akward to use keys or sibling recursion. - Martin Honnenxsl:iterateallows that kind of. - Martin Honnenversionyou declare, you can usefor-each-group group-starting-withand nest afor-each-group group-ending-withto solve that, have a look at stackoverflow.com/tags/xslt-grouping/info - Martin Honnen