Search code examples
xmlxslttei

Error message when attempting to group items in xsl


I'm attempting to group every two tei:div elements of the following xml (which is part of a larger file containing my CV), and wrap them in a <div type="reference_block">:

     <div type="category">
        <head n="references">References</head>
        <div type="reference" xml:id="1">
              <persName>
                 <forename type="first">firstname1</forename>
                 <surname>surname1</surname>
              </persName> 
           <address>
                 <name type="building">building1</name>
                 <postBox>postbox1</postBox>
                 <placeName>
                    <settlement>settlement1</settlement>
                    <region>region1</region>
                    <country>country1</country>
                 </placeName>
                 <postCode>postcode1</postCode>
           </address>
           <num n="telephone">telephone1</num>
           <email>email1</email>
        </div>
        <div type="reference" xml:id="2">
              <persName>
                 <forename type="first">firstname2</forename>
                 <surname>surname2</surname>
              </persName> 
           <address>
                 <name type="building">building2</name>
                 <postBox>postbox2</postBox>
                 <placeName>
                    <settlement>settlement2</settlement>
                    <region>region2</region>
                    <country>country2</country>
                 </placeName>
                 <postCode>postcode2</postCode>
           </address>
           <num n="telephone">telephone2</num>
           <email>email2</email>
        </div>
        <div type="reference" xml:id="3">
              <persName>
                 <forename type="first">firstname3</forename>
                 <surname>surname3</surname>
              </persName> 
           <address>
                 <name type="building">building3</name>
                 <postBox>postbox3</postBox>
                 <placeName>
                    <settlement>settlement3</settlement>
                    <region>region3</region>
                    <country>country3</country>
                 </placeName>
                 <postCode>postcode3</postCode>
           </address>
           <num n="telephone">telephone3</num>
           <email>email3</email>
        </div>
        <div type="reference" xml:id="4">
              <persName>
                 <forename type="first">firstname4</forename>
                 <surname>surname4</surname>
              </persName> 
           <address>
                 <name type="building">building4</name>
                 <postBox>postbox4</postBox>
                 <placeName>
                    <settlement>settlement4</settlement>
                    <region>region4</region>
                    <country>country4</country>
                 </placeName>
                 <postCode>postcode4</postCode>
           </address>
           <num n="telephone">telephone4</num>
           <email>email4</email>
        </div>
        <div type="reference" xml:id="5">
              <persName>
                 <forename type="first">firstname5</forename>
                 <surname>surname5</surname>
              </persName> 
           <address>
                 <name type="building">building5</name>
                 <postBox>postbox5</postBox>
                 <placeName>
                    <settlement>settlement5</settlement>
                    <region>region5</region>
                    <country>country5</country>
                 </placeName>
                 <postCode>postcode5</postCode>
           </address>
           <num n="telephone">telephone5</num>
           <email>email5</email>
        </div>
        <div type="reference" xml:id="6">
              <persName>
                 <forename type="first">firstname6</forename>
                 <surname>surname6</surname>
              </persName>
           <address>
                 <name type="building">building6</name>
                 <postBox>postbox6</postBox>
                 <placeName>
                    <settlement>settlement6</settlement>
                    <region>region6</region>
                    <country>country6</country>
                 </placeName>
                 <postCode>postcode6</postCode>
           </address>
           <num n="telephone">telephone6</num>
           <email>email6</email>
        </div>
        <div type="reference" xml:id="7">
              <persName>
                 <forename type="first">firstname7</forename>
                 <surname>surname7</surname>
              </persName>
           <address>
                 <name type="building">building7</name>
                 <postBox>postbox7</postBox>
                 <placeName>
                    <settlement>settlement7</settlement>
                    <region>region7</region>
                    <country>country7</country>
                 </placeName>
                 <postCode>postcode7</postCode>
           </address>
           <num n="telephone">telephone7</num>
           <email>email7</email>
        </div>
     </div>

I'm using this template to attempt to wrap the items:

 <xsl:template match="tei:div[@type='reference']">
    <xsl:if test="@xml:id mod 2 = 1">
        <div class="reference_block">
            <xsl:apply-templates select=".|following-sibling::tei:div[@type='reference'][@xml:id &lt; 2]"/>
        </div>
       </xsl:if>
    </xsl:template>

When I do so, however, I'm getting an error message: "Too many nested apply-templates calls. The stylesheet may be looping"

The problem appears to be the "." in my select, but I'm not sure why I'm getting the error message. Maybe it's because it's calling itself and I don't realize it? Could someone give me some insight into why this might be happening, or an alternative way to group the items that doesn't require the "." select? Thanks!


Solution

  • The "." is indeed causing the issue, as it represents the current context node (i.e the div node you have currently matched), and so selecting it will just cause the same template to be matched, and within the template the test="@xml:id mod 2 = 1" will still be true, leading to infinite recursion.

    If you just want to copy the current div and the following div, just replace xsl:apply-templates with xsl:copy-of instead. Do note, your expression for getting the following sibling is not quite right either. You want to be comparing the id of the next element with the id of the current element

    Try this...

     <xsl:copy-of select=".|following-sibling::tei:div[@type='reference'][@xml:id &lt; current()/@xml:id + 2]"/>
    

    Or, if the ids were always going to be in order, this would work too

    <xsl:copy-of select=".|following-sibling::tei:div[@type='reference'][1]"/>
    

    On the other hand, if you wanted to do some further transformation on the pair of div elements you are selecting, you could make use of the mode attribute, so a different template is matching

    <xsl:template match="tei:div[@type='reference']">
       <xsl:if test="@xml:id mod 2 = 1">
          <div class="reference_block">
            <xsl:apply-templates select=".|following-sibling::tei:div[@type='reference'][1]" mode="group"/>
          </div>
       </xsl:if>
    </xsl:template>
    
    <xsl:template match="tei:div" mode="group">
        <!-- Other code here -->
    </xsl:template>