I have an XML without structural markup and need to extract all nodes in between two nodes with specific strings. Here my XML:
<singlenode>
<title>C-2.6.8</title>
<p><b>Test Objective:</b> Verify that reprinting partial labels is possible.</p>
<p><b>Acceptance Criterion:</b> Partial unit labels are reprinted correctly.</p>
<p><b>Prerequisite:</b> You are currently running a <b>track and trace</b> test order with
partial label printing enabled.</p>
<p><b>Test Note:</b></p>
<ul type="disc">
<li>
<p><b>Line:</b> This test assumes an automatic device for aggregating items to
cases.</p>
</li>
</ul>
<p/>
</singlenode>
In bewtween the nodes containing the strings "Test Objective" and "Acceptance Criterion" can be a combination of tags, the same goes for the tags between the node containing "Acceptance Criterion" and "Prerequisite". How do I extrac all tags until the node contains a specific string?
I have tried to name all tags that might occur, but due to the wild combination of tags, this gives unwanted results.
My XSLT (3.0):
<testcases>
<testcase>
<xsl:attribute name="internalid"/>
<xsl:attribute name="name">
<xsl:value-of select="title"/>
</xsl:attribute>
<node_order/>
<externalid/>
<version/>
<summary> <xsl:text disable-output-escaping="yes"><![CDATA[</xsl:text>
<xsl:for-each select="/singlenode/p/b[contains(text(),'Test Objective') or contains(text(),'Test Objectives')]">
<xsl:copy-of select=".."/>
<xsl:copy-of select="/singlenode/p/b[contains(text(),'Test Objective') or contains(text(),'Test Objectives')]/following::ul[1]"/>
<xsl:copy-of select="/singlenode/p/b[contains(text(),'Test Objective') or contains(text(),'Test Objectives')]/following::ol[1]"/>
</xsl:for-each>
<xsl:for-each select="/singlenode/p/b[contains(text(),'Acceptance Criterion') or contains(text(),'Acceptance Criteria')]">
<xsl:copy-of select=".."/>
<xsl:copy-of select="/singlenode/p/b[contains(text(),'Acceptance Criterion') or contains(text(),'Acceptance Criteria')]/following::ul[1]"/>
<xsl:copy-of select="/singlenode/p/b[contains(text(),'Acceptance Criterion') or contains(text(),'Acceptance Criteria')]/following::ol[1]"/>
</xsl:for-each>
<xsl:for-each select="/singlenode/p/b[contains(text(),'Test Instruction') or contains(text(),'Test Instructions')]">
<xsl:copy-of select=".."/>
<xsl:copy-of select="/singlenode/p/b[contains(text(),'Test Instruction') or contains(text(),'Test Instructions')]/following::ul[1]"/>
<xsl:copy-of select="/singlenode/p/b[contains(text(),'Test Instruction') or contains(text(),'Test Instructions')]/following::ol[1]"/>
</xsl:for-each>
<xsl:for-each select="/singlenode/p/b[contains(text(),'Test Notes') or contains(text(),'Note') or contains(text(),'Notes' )or contains(text(),'Test notes')]">
<xsl:copy-of select=".."/>
<xsl:copy-of select="/singlenode/p/b[contains(text(),'Test Notes') or contains(text(),'Note') or contains(text(),'Notes' )or contains(text(),'Test notes')]/following::ul[1]"/>
<xsl:copy-of select="/singlenode/p/b[contains(text(),'Test Notes') or contains(text(),'Note') or contains(text(),'Notes' )or contains(text(),'Test notes')]/following::ol[1]"/>
</xsl:for-each>
<xsl:text disable-output-escaping="yes">]]></xsl:text>
</summary>
<preconditions>
<xsl:for-each select="/singlenode/p/b[contains(text(),'Prerequisite') or contains(text(), 'Prerequisites')]">
<xsl:text disable-output-escaping="yes"><![CDATA[<p></xsl:text>
<xsl:copy-of select=".."/>
<xsl:copy-of select="/singlenode/p/b[contains(text(),'Prerequisite') or contains(text(), 'Prerequisites')]/following::ul[1]"/>
<xsl:copy-of select="/singlenode/p/b[contains(text(),'Prerequisite') or contains(text(), 'Prerequisites')]/following::ol[1]"/>
<xsl:text disable-output-escaping="yes"></p>]]></xsl:text>
</xsl:for-each> </preconditions>
</testcase>
</testcases>
</xsl:template>
Gives me the result:
<testcases>
<testcase internalid=""
name="C-2.6.8">
<node_order/>
<externalid/>
<version/>
<summary><![CDATA[<p>
<b>Test Objective:</b> Verify that reprinting partial labels is possible.</p>
<ul type="disc">
<li>
<p>
<b>Line:</b> This test assumes an automatic device for aggregating items to
cases.</p>
</li>
</ul>
<p>
<b>Acceptance Criterion:</b> Partial unit labels are reprinted correctly.</p>
<ul type="disc">
<li>
<p>
<b>Line:</b> This test assumes an automatic device for aggregating items to
cases.</p>
</li>
</ul>
<p>
<b>Test Note:</b>
</p>
<ul type="disc">
<li>
<p>
<b>Line:</b> This test assumes an automatic device for aggregating items to
cases.</p>
</li>
</ul>]]></summary>
<preconditions><![CDATA[<p><p>
<b>Prerequisite:</b> You are currently running a <b>track and trace</b> test order with
partial label printing enabled.</p>
<ul type="disc">
<li>
<p>
<b>Line:</b> This test assumes an automatic device for aggregating items to
cases.</p>
</li>
</ul></p>]]></preconditions>
</testcase>
</testcases>
Expected result is that the "Test Note" shall be written only into the summary.
How do I extract exactly the nodes between the <p>s with strings "Test Objective", "Acceptance Criterion" and "Prerequisites"?
<<and>>to select based on document order (simply remember that inside XSLT code you need to escape any<as<. Often such problems can also be solved using a combinatation offor-each-group starting-with/ending-with. I have not quite understood which result you want, you might want to simplify the samples but show the exact result you want, perhaps in a first step without the CDATA stuff as that, with XSLT 3, is probably better solved by using theserializefunction andcdata-section-elements, once we have established which nodes you want to select. - Martin Honnen