I am attempting to write XSLT that will run a for-each on the selected following-siblings but stop when another tag (h1) is reached.
Here's the Source XML:
<?xml version="1.0"?>
<html>
    <h1>Test</h1>
    <p>Test: p 1</p>
    <p>Test: p 2</p>
    <h1>Test 2</h1>
    <p>Test2: p 1</p>
    <p>Test2: p 2</p>
    <p>Test2: p 3</p>
</html>
Here's the XSLT:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes"/>
    <xsl:template match="/">
        <content>
            <xsl:apply-templates/>
        </content>
    </xsl:template>
    <xsl:template match="h1">
        <section>
            <sectionHeading>
                <xsl:apply-templates/>
            </sectionHeading>
            <sectionContent>
                <xsl:for-each select="following-sibling::p">
                    <paragraph>
                        <xsl:value-of select="."/>
                    </paragraph>
                </xsl:for-each>
            </sectionContent>
        </section>
    </xsl:template>
    <xsl:template match="p"/>
</xsl:stylesheet>
Here's the current result:
<?xml version="1.0" encoding="UTF-8"?>
<content>
    <section>
        <sectionHeading>Test</sectionHeading>
        <sectionContent>
            <paragraph>Test: p 1</paragraph>
            <paragraph>Test: p 2</paragraph>
            <paragraph>Test: p 3</paragraph>
            <paragraph>Test2: p 1</paragraph>
            <paragraph>Test2: p 2</paragraph>
        </sectionContent>
    </section>
    <section>
        <sectionHeading>Test 2</sectionHeading>
        <sectionContent>
            <paragraph>Test2: p 1</paragraph>
            <paragraph>Test2: p 2</paragraph>
        </sectionContent>
    </section>
</content>
Here's the expected result:
<?xml version="1.0" encoding="UTF-8"?>
<content>
<section>
    <sectionHeading>Test</sectionHeading>
    <sectionContent>
        <paragraph>Test: p 1</paragraph>
        <paragraph>Test: p 2</paragraph>
        <paragraph>Test: p 3</paragraph>
    </sectionContent>
</section>
<section>
    <sectionHeading>Test 2</sectionHeading>
    <sectionContent>
        <paragraph>Test2: p 1</paragraph>
        <paragraph>Test2: p 2</paragraph>
    </sectionContent>
</section>
</content>
The following-sibling axis indicates all the nodes that have the same parent as the context node and appear after the context node in the source document.
Long answer: The value of attributes cannot contain a few special characters, such as '<' , '>' and '&' . If present, they are escaped as: '<' , '>' and '&' . These characters can be produced if the output method is 'text', which is not your case.
XSLT doesn't have any new function to search Strings in a reverse manner. We have substring function which creates two fields substring-before-last and substring-after-last.In XSLT it is defined as follows: <xsl:value-of select="substring (string name ,0, MAX_LENGTH )"/>...
Try this: (Instead of asking for all the p's we ask for all the p's whose most recently preceding h1 is current.)
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes"/>
    <xsl:template match="/">
        <content>
            <xsl:apply-templates/>
        </content>
    </xsl:template>
    <xsl:template match="h1">
        <xsl:variable name="header-id" select="generate-id(.)"/>
        <section>
            <sectionHeading>
                <xsl:apply-templates/>
            </sectionHeading>
            <sectionContent>
                <xsl:for-each select="following-sibling::p[generate-id(preceding-sibling::h1[1]) = $header-id]">
                    <paragraph>
                        <xsl:value-of select="."/>
                    </paragraph>
                </xsl:for-each>
            </sectionContent>
        </section>
    </xsl:template>
    <xsl:template match="p"/>
</xsl:stylesheet>
Further in this post, i will explain the real compare of the following essential statement and why it can and will fail.
recap/analyse situation while being in template <xsl:template match="h1">:
h1 from the matching <xsl:template>.header contains a duplicate of my current context node.The essential statement which is bad/wrong:
following-sibling::p[preceding-sibling::h1[1] = $header]
p of my context node | following-sibling::p
p where the first (closest) preceding-sibling named h1 "is" the same as the variable $header | ...[preceding-sibling::h1[1] = $header].!! In XSLT 1.0 the compare of a node with a node will be done by its value !!
See it in an example. Lets pretend the input xml is like this [<h1> contain twice the same value Test]:
<html>
    <h1>Test</h1>
    <p>Test: p 1</p>
    <p>Test: p 2</p>
    <h1>Test</h1>
    <p>Test2: p 1</p>
    <p>Test2: p 2</p>
    <p>Test2: p 3</p>
</html>
A !WRONG! result will be created:
<content>
  <section>
     <sectionHeading>Test</sectionHeading>
     <sectionContent>
        <paragraph>Test: p 1</paragraph>
        <paragraph>Test: p 2</paragraph>
        <paragraph>Test2: p 1</paragraph> <-- should be only in 2. section 
        <paragraph>Test2: p 2</paragraph> <-- should be only in 2. section 
        <paragraph>Test2: p 3</paragraph> <-- should be only in 2. section 
     </sectionContent>
  </section>
  <section>
     <sectionHeading>Test</sectionHeading>
     <sectionContent>
        <paragraph>Test2: p 1</paragraph>
        <paragraph>Test2: p 2</paragraph>
        <paragraph>Test2: p 3</paragraph>
     </sectionContent>
  </section>
</content>
...
<xsl:template match="h1">
    <xsl:variable name="header" select="generate-id(.)"/>
    <section>
        <sectionHeading>
            <xsl:apply-templates/>
        </sectionHeading>
        <sectionContent>
            <xsl:for-each select="following-sibling::p[generate-id(preceding-sibling::h1[1]) = $header]">
                <paragraph>
                    <xsl:value-of select="."/>
                </paragraph>
            </xsl:for-each>
        </sectionContent>
    </section>
</xsl:template>
...
Use the function generate-id() to get the unique (at least in the current document) ID of a node and compare now node vs node! Even if you use this technique with <xsl:key>, you have to use generate-id().
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With