Reputation: 1399
This might be a novice question but, then again, I'm a novice :-)
I have XML files in this format:
<chapter>
<title>chapter title</title>
<para>p1</para>
<para>p2</para>
<para>p3</para>
<!-- ....and so on for a lot of para elements -->
</chapter>
...which are handled by an XSL template like this...
<xsl:template match="para">
<xsl:if test="count(following-sibling::para) = 1 and count(preceding-sibling::para) > 13">
<!-- Insert Some Stuff -->
</xsl:if>
</xsl:template>
The logic here is: "Insert Some Stuff" before the second-to-last element if there are enough preceding <para> siblings.
This code gets the job done and works fine.
Now, for reasons beyond my control, I need to adapt that template to handle the following kind of files as well...
<chapter>
<title>chapter title</title>
<para>p1</para>
<para>p2</para>
<para>p3</para>
<section>
<para>p4</para>
<para>p5</para>
</section>
<para>p6</para>
<section>
<title>section title</title>
</section>
<para>p7</para>
<section>
<para>p8</para>
</section>
<!-- ....and so on for a lot of para elements -->
</chapter>
The difference with these files is that the <section> element can appear randomly, sometimes containing <para> elements and sometimes not. There is no way of predicting when and where <section> elements will appear.
I need the original XSL template to work with this format in the same way despite the <section> elements being there. So that means that the <para> elements still need to be counted in the same way even if they are sometimes children and sometimes siblings.
In summary, I need the logic of the old code to work as before, as though it is completely ignoring the presence of the <section> elements.
What should the original template <xsl:if test="..."> be rewritten as to make this work?
Performance is not an issue - these are manually-run ad-hoc transformations.
XSLT 1.0 only, please
Thanks.
Upvotes: 0
Views: 75
Reputation: 116959
It is difficult to answer your question without seeing a complete stylesheet and the expected output/s.
Would not:
<xsl:template match="para">
<xsl:if test="count(following::para) = 1 and count(preceding::para) > 13">
<!-- Insert Some Stuff -->
</xsl:if>
</xsl:template>
work for you?
If you're sure that what you've suggested should work for the example given in the question
Well, the example given in the question does not have the required minimum of para
nodes - but if we reduce the threshold:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="para">
<xsl:if test="count(following::para) = 1 and count(preceding::para) > 5">
<INSERTED_NODE/>
</xsl:if>
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Test Input A
<chapter>
<title>Section Title 1</title>
<para>Para 1</para>
<para>Para 2</para>
<para>Para 3</para>
<section>
<title>Section Title 2</title>
<para>Para 4</para>
<para>Para 5</para>
</section>
<para>Para 6</para>
<section>
<title>Section Title 3</title>
<para>Para 7</para>
<para>Para 8</para>
</section>
<para>Para 9</para>
<para>Para 10</para>
</chapter>
Result A
<?xml version="1.0" encoding="UTF-8"?>
<chapter>
<title>Section Title 1</title>
<para>Para 1</para>
<para>Para 2</para>
<para>Para 3</para>
<section>
<title>Section Title 2</title>
<para>Para 4</para>
<para>Para 5</para>
</section>
<para>Para 6</para>
<section>
<title>Section Title 3</title>
<para>Para 7</para>
<para>Para 8</para>
</section>
<INSERTED_NODE/>
<para>Para 9</para>
<para>Para 10</para>
</chapter>
Test Input B
<chapter>
<title>Section Title 1</title>
<para>Para 1</para>
<para>Para 2</para>
<para>Para 3</para>
<section>
<title>Section Title 2</title>
<para>Para 4</para>
<para>Para 5</para>
</section>
<para>Para 6</para>
<section>
<title>Section Title 3</title>
<para>Para 7</para>
<para>Para 8</para>
</section>
<para>Para 9</para>
</chapter>
Result B
<?xml version="1.0" encoding="UTF-8"?>
<chapter>
<title>Section Title 1</title>
<para>Para 1</para>
<para>Para 2</para>
<para>Para 3</para>
<section>
<title>Section Title 2</title>
<para>Para 4</para>
<para>Para 5</para>
</section>
<para>Para 6</para>
<section>
<title>Section Title 3</title>
<para>Para 7</para>
<INSERTED_NODE/>
<para>Para 8</para>
</section>
<para>Para 9</para>
</chapter>
Is there a way to force it to be inserted once per chapter element?
Yes, you could do it this way:
<xsl:template match="para">
<xsl:variable name="i">
<xsl:number count="para" from="chapter" level="any"/>
</xsl:variable>
<xsl:variable name="n" select="count(ancestor::chapter//para)" />
<xsl:if test="$i + 1 = $n and $n > 5">
<INSERTED_NODE/>
</xsl:if>
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
A more efficient solution would do the counting once at the chapter level, and pass it down as a parameter to the paras.
Upvotes: 1