XSLT to find all related elements

80 views Asked by At

I am trying to find out if a XSLT can be used to transform the following XML (snippet as it contains more elements) so that the transformed XML only contains the content elements that are specified in the child element.

<?xml version="1.0" encoding="UTF-8"?>
<library>
    <content content-id="a">
        <content-link content-id="a1"/>
        <content-link content-id="a2"/>
    </content>
    <content content-id="b">
        <content-link content-id="b1"/>
        <content-link content-id="b2"/>
    </content>
    <content content-id="a1">
        <content-link content-id="a11"/>
        <content-link content-id="a12"/>
    </content>
    <content content-id="a2">
        <content-link content-id="a21"/>
        <content-link content-id="a22"/>
    </content>
    <content content-id="a11"/>
    <content content-id="a12"/>
    <content content-id="a21"/>
    <content content-id="a22"/>    
</library>

to

<?xml version="1.0" encoding="UTF-8"?>
<library>
    <content content-id="a">
        <content-link content-id="a1"/>
        <content-link content-id="a2"/>
    </content>
    <content content-id="a1">
        <content-link content-id="a11"/>
        <content-link content-id="a12"/>
    </content>
    <content content-id="a2">
        <content-link content-id="a21"/>
        <content-link content-id="a22"/>
    </content>
    <content content-id="a11"/>
    <content content-id="a12"/>
    <content content-id="a21"/>
    <content content-id="a22"/>    
</library>

Below is my attempt but I am rather weak in XSLT so appreciate further inputs here

<?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:variable name="cid" select="'a'" />
    <xsl:template match="/">
        <library>        
        <xsl:apply-templates select="//content[@content-id=$cid]"/>        
        </library>
    </xsl:template>
    
    <xsl:template match="content">        
        <xsl:copy-of select="." />        
        <xsl:for-each select="*">
            <xsl:apply-templates select="content-link"/>
        </xsl:for-each>
    </xsl:template>
    
    <xsl:template match="content-link">
         <!-- Don't know how to call the ancestor content elements referenced by content-link -->
    </xsl:template>
</xsl:stylesheet>
1

There are 1 answers

4
Martin Honnen On BEST ANSWER

Perhaps like this:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:mf="http://example.com/mf"
    exclude-result-prefixes="#all"
    version="3.0">
    
  <xsl:function name="mf:get-related-elements" as="element()*">
      <xsl:param name="element" as="element()"/>
      <xsl:sequence
        select="$element ! (. | content-link | key('ref', content-link/@content-id)/mf:get-related-elements(.))"/>
  </xsl:function>
  
  <xsl:key name="ref" match="/*//*" use="@content-id"/>
    
  <xsl:param name="cid" select="'a'" />
  
  <xsl:variable name="start" select="key('ref', $cid)"/>
  <xsl:variable name="related-elements" select="mf:get-related-elements($start)"/>

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:template match="/*//*[not(. intersect $related-elements)]"/>
  
</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/6q1SDjU