Search code examples
xslttei

xml:id of ancestor element


My stylesheet used version="3.0". My engine version Saxon PEE 9.9.1.7.

The xml:id attribute of w is built from xml:id of l + '_' + the text() value of w. It seems to me that the xpath of xml:id of l is correct, but this doesn't work. Example of xml before transformation:

<div3 xml:id="ktu1-1_ii">
   <!-- tei elements div4 and lg -->
         <l n="1a">
            <!-- other children -->
            <w>l</w>
         </l>

Processing instructions:

   <xsl:template match="div3/div4/lg/l">
      <xsl:copy>
         <xsl:apply-templates select="@*"/>
         <xsl:if test="not(@xml:id)">
            <xsl:attribute name="xml:id">
               <xsl:value-of select="concat(ancestor::div3/@xml:id, '_l', @n)"/>
            </xsl:attribute>
         </xsl:if>
         <xsl:apply-templates/>
      </xsl:copy>
   </xsl:template>
   
 
   <xsl:template match="l/w">
      <xsl:variable name="xmlid">
         <xsl:value-of select="ancestor::l/@xml:id"/>
      </xsl:variable>
      <xsl:message select="$xmlid"/> <!-- test @xml:id, nothing -->
      <xsl:copy>
         <xsl:apply-templates select="@*"/>
         <xsl:attribute name="pos"></xsl:attribute>
         <!-- Calculate xml:id based on parent 'l' xml:id -->
         <xsl:attribute name="xml:id">
            <xsl:value-of select="normalize-space(replace(ancestor::l/@xml:id, ' ', '')) || '_' || replace(., '\s|̊', '')"/> 
         </xsl:attribute>
         <xsl:apply-templates/>
      </xsl:copy>
   </xsl:template> 

The result is correct for l: <l n='1a' xml:id='ktu1-1_ii_l1a'>, but only one part works for w <w xml:id="_l">l</w> when the result should be <w xml:id="ktu1-1_ii_l1a_l">l</w>.

Any idea where the error might have come from?


Solution

  • Consider the following (simplified) example:

    XML

    <div3 xml:id="ktu1-1_ii">
        <!-- tei elements div4 and lg -->
        <l n="1a">
            <!-- other children -->
            <w>l</w>
            <w>l</w>
        </l>
    </div3>
    

    XSLT 3.0

    <xsl:stylesheet version="3.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="*"/>
    
    <xsl:mode on-no-match="shallow-copy"/>
      
    <xsl:template match="l[not(@xml:id)]">
        <xsl:variable name="l_id" select="concat(ancestor::div3/@xml:id, '_l', @n)"/>
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:attribute name="xml:id" select="$l_id"/>   
            <xsl:apply-templates>
                <xsl:with-param name="l_id" select="$l_id"/>
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>
    
    <xsl:template match="w">
        <xsl:param name="l_id"/>
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:attribute name="xml:id" select="concat($l_id, '_', position())"/>  
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>
    
    </xsl:stylesheet>
    

    Result

    <?xml version="1.0" encoding="UTF-8"?>
    <div3 xml:id="ktu1-1_ii"><!-- tei elements div4 and lg -->
       <l n="1a" xml:id="ktu1-1_ii_l1a"><!-- other children -->
          <w xml:id="ktu1-1_ii_l1a_2">l</w>
          <w xml:id="ktu1-1_ii_l1a_3">l</w>
       </l>
    </div3>
    

    Here the value of the l parent's xml:id is calculated once and passed down to the children as a parameter.


    P.S. The purpose of this exercise is not entirely clear. If you need to have a unique ID for each node, you could simply have your stylesheet generate them instead of trying to assemble them piecemeal from their ancestor parts.