xsl-list
[Top] [All Lists]

Re: [xsl] How to extract an element plus all the elements it references plus all the elements referenced by the referenced elements plus ...

2018-06-08 13:26:52
On 08.06.2018 20:19, Costello, Roger L. costello(_at_)mitre(_dot_)org wrote:

I have instance documents containing a bunch of <airport> elements. I want to extract one of the 
<airport> elements. But the <airport> element might contain IDREF elements, so the result 
document must include the <airport> element and the referenced elements. Those referenced 
elements might contain IDREF elements, so the result document must include those referenced elements as 
well. And on and on and on. Further, while collecting those referenced elements, there might be a 
reference to an element that has already been collected; I don't want to collect elements twice.

I would like to extract the <airport> element with ICAO = KBOS (Boston airport). The 
following result document contains the desired <airport> element plus all the 
recursively referenced elements:

I've written an XSLT program to do the job. See below. I am wondering if there 
is a better (simpler, clearer) solution?

You could use a key

  <xsl:key name="ref" match="Airports/*" use="id"/>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
     xmlns:f="function"
     exclude-result-prefixes="f"
     version="2.0">
<xsl:output method="xml" /> <xsl:variable name="doc" select="/"/> <xsl:template match="Airports">
         <Results>
             <xsl:sequence 
select="f:get-elements-and-their-referenced-elements(airport[ICAO eq 'KBOS'], foo)" 
/>

then instead of airport[ICAO eq 'KBOS'] you can use key('ref', 'KBOS')

         </Results>
     </xsl:template>
<xsl:function name="f:get-elements-and-their-referenced-elements" as="element()*">
         <xsl:param name="elements-to-process" as="element()*" />
         <xsl:param name="processed-elements" as="element()*" />
<xsl:choose>
             <xsl:when test="not($elements-to-process)">
                 <xsl:sequence select="$processed-elements" />
             </xsl:when>
             <xsl:otherwise>
                 <xsl:variable name="referenced-elements" as="element()*">
                     <xsl:for-each select="$elements-to-process//ref">
                         <xsl:variable name="ref" select="." />
                         <xsl:sequence select="$doc//*[@id eq $ref]" />
                     </xsl:for-each>
                 </xsl:variable>

and here you could use

<xsl:variable name="referenced-elements" select="key('ref', $elements-to-process//ref, $doc)"/>

                 <xsl:variable name="new-elements-to-process" 
select="$referenced-elements except $processed-elements" />
                 <xsl:variable name="new-processed-elements" 
select="$processed-elements union $new-elements-to-process" />
                 <xsl:sequence 
select="f:get-elements-and-their-referenced-elements($new-elements-to-process, 
$new-processed-elements)" />
             </xsl:otherwise>
         </xsl:choose>
</xsl:function>
--~----------------------------------------------------------------
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
EasyUnsubscribe: http://lists.mulberrytech.com/unsub/xsl-list/1167547
or by email: xsl-list-unsub(_at_)lists(_dot_)mulberrytech(_dot_)com
--~--

<Prev in Thread] Current Thread [Next in Thread>