Same tricks from
Re: Get Node Value from irregular XML (xpather.pl) "//header[ contains(.,'rationale') ]/* "
xmllint.exe --xpath " //header[ contains(.,'rationale') ]/l /preceding
+-sibling::* " fudge
xmllint.exe --xpath " //header[ contains(.,'rationale') ]/child::text(
+) " fudge
where fudge is your data
part=1 part=2 <fudge>
<root>
<part>
<sect>
<header>
1. Purpose and rationale
<p>purpose 1</p>
<p>Purpose 2</p>
<p>purpose 3</p>
<l>
<li>purpose list 1</li>
<li>list2</li>
</l>
</header>
</sect>
</part>
</root>
<root>
<part>
<sect>
<header>
2. Purpose and rationale
<p>2 purpose 1</p>
<p>2 Purpose 2</p>
<p>2 purpose 3</p>
<l>
<li>2 purpose list 1</li>
<li>2 list2</li>
</l>
<p>2. Some other heading</p>
<p>2 content 1</p>
<p>2 content 2</p>
</header>
</sect>
</part>
</root>
</fudge>
The two queries combined, with the results
xmllint.exe --xpath " //header[ contains(.,'rationale') ]/child::text(
+) | //header[ contains(.,'rationale') ] /l/preceding-sibling::* " f
+udge
1. Purpose and rationale
<p>purpose 1</p>
<p>Purpose 2</p>
<p>purpose 3</p>
2. Purpose and rationale
<p>2 purpose 1</p>
<p>2 Purpose 2</p>
<p>2 purpose 3</p>