Re: [xsl] Can I use xsl:key to select elements up to certain ancestor

Subject: Re: [xsl] Can I use xsl:key to select elements up to certain ancestor
From: "Syd Bauman s.bauman@xxxxxxxxxxxxxxxx" <xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx>
Date: Wed, 30 Aug 2017 19:19:16 -0000
I think there may be a more efficient way to do this overall, but
certainly if you declare those @id attributes as ID, then you could
use the id() function. E.g.:

---------
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
                xmlns:xs="http://www.w3.org/2001/XMLSchema"; version="2.0">

  <xsl:output method="xml" indent="yes" omit-xml-declaration="no"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="p">
    <xsl:variable name="topicID" select="ancestor-or-self::*[@id][1]/@id"/>
    <xsl:variable name="ancestors-in-topic"
                        select="ancestor-or-self::*[
        ancestor-or-self::* intersect id($topicID)
        ]"/>
    <xsl:copy>
      <xsl:attribute name="topic" select="$topicID"/>
      <xsl:text>Ancestors: </xsl:text>
      <xsl:sequence select="$ancestors-in-topic/name()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="* | @*">
    <xsl:copy>
      <xsl:apply-templates select="@*, node()"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>
---------

works iff you declare the @id attrs as type ID:

---------
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE bars [
<!ELEMENT bars ( bar+ )>
<!ATTLIST bars id ID #IMPLIED>

<!ELEMENT bar ( bar | div )+>
<!ATTLIST bar id ID #IMPLIED>

<!ELEMENT div ( bar | div | p )+>
<!ELEMENT p EMPTY >
]>
<bars id="x0">
  <bar id="x1">
    <bar id="x11">
      <bar id="x12">
        <div>
          <bar id="x13">
            <div>
              <p/>
            </div>
          </bar>
        </div>
      </bar>
    </bar>
  </bar>
</bars>
---------

> I had an embarrassing moment today, when I found that the source of
> a severe performance problem was sitting in front of the screen.
> 
> Using XSLT 2.0 I am transforming elements and at many points I have
> to look up the ancestor axis, but only up to an element with an
> attribute "id". Basically I have a deeply nested structure of
> topics and I want to analyze ancestors inside the current topic.
> 
> The nearest topic is easy to find: 
> 
> <xsl:variable name="topic" select="ancestor-or-self::*[@id][1]" as="element()"/>
> 
> And then I was doing the following to find all ancestors that share
> the same "topic ancestor":
> 
> <xsl:variable name="ancestors-in-topic" 
>   select="ancestor-or-self::*[ancestor-or-self::* = $topic]" as="element()*"/>
> 
> This did not what I expected and also wasted a lot of resources
> because the predicate was true for all or most elements. After some
> testing I went with this:
> 
> <xsl:variable name="ancestors-in-topic" 
>   select="ancestor-or-self::*[ancestor-or-self::*/generate-id() = generate-id($topic)]" as="element()*"/>
> 
> Since that calculation is done very often I am wondering if xsl:key
> could be used to speed things up?
> 
> Any pointers are very welcome, as always, thanks,
> 
> - Michael
> 
> PS: My sample XML and XSLT below.
> 
> <?xml version="1.0" encoding="UTF-8"?>
> <bars id="x0">
>   <bar id="x1">
>     <bar id="x11">
>       <bar id="x12">
>         <div>
>           <bar id="x13">
>             <div>
>               <p/>
>             </div>
>           </bar>
>         </div>
>       </bar>
>     </bar>
>   </bar>
> </bars>
> 
> 
> <?xml version="1.0" encoding="UTF-8"?>
> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"; xmlns:xs="http://www.w3.org/2001/XMLSchema"; version="2.0">
>   <xsl:output method="xml" indent="yes" omit-xml-declaration="no"/>
>   <xsl:strip-space elements="*"/>
> 
>   <xsl:template match="p">
>     <xsl:variable name="topic" select="ancestor-or-self::*[@id][1]" as="element()"/>
>     <xsl:variable name="ancestors-in-topic" select="ancestor-or-self::*[ancestor-or-self::*/generate-id() = generate-id($topic)]" as="element()*"/>
>     <xsl:copy>
>       <xsl:attribute name="topic" select="$topic/@id"/>
>       <xsl:text>Ancestors: </xsl:text>
>        <xsl:sequence select="$ancestors-in-topic/name()"/> 
>     </xsl:copy>
>   </xsl:template>
> 
>   <xsl:template match="* | @*">
>     <xsl:copy>
>       <xsl:apply-templates select="@*, node()"/>
>     </xsl:copy>
>   </xsl:template>
> </xsl:stylesheet>
> 
> 
> Expected output for <p>: <p topic="x13">Ancestors: bar div p</p>
> 

-- 
 Syd Bauman, EMT-Paramedic
 Senior XML Programmer/Analyst
 Northeastern University Women Writers Project
 s.bauman@xxxxxxxxxxxxxxxx or
 Syd_Bauman@xxxxxxxxxxxxxxxx

Current Thread