2

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>
Roy Tan
  • 25
  • 2

1 Answers1

1

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

Martin Honnen
  • 160,499
  • 6
  • 90
  • 110
  • Thanks Martin, your XSLT definitely help! I just realized my content-link element is a grandchild as per below example snippet: However I am not skilled enough to make the right changes so wondering if you can help here. – Roy Tan Oct 23 '20 at 08:13
  • I managed to solve it with the changes as per https://xsltfiddle.liberty-development.net/jxNakA5 The only outstanding issue I am not able to output other elements except for and . Certainly appreciate if you can help point out what needs to be modified here. – Roy Tan Oct 23 '20 at 11:18
  • @RoyTan, in general don't try to change your requirements after an answer. Instead, ask a new question with the necessary details. As for the example you linked to, check whether the change in https://xsltfiddle.liberty-development.net/jxNakA5/1 does what you want. If not, please ask a new question detailing the possible input structure and the rules for which elements to copy and which not. – Martin Honnen Oct 23 '20 at 12:24
  • Thanks Martin, noted on posting a new question next time. Updated change in http://xsltfiddle.liberty-development.net/jxNakA5/1 solves the issues so all good here. – Roy Tan Oct 23 '20 at 13:09