Search code examples
templatesxsltxpathnodes

Applying template on differents node's level


I try to modify all the node's QName which contain a certain string : and return new one without.

(e.g. I have <RandomName> and want to render <Name>)

I have a xsl:template working for the first level node, but can't find how to apply it to lower levels.

e.g. XML Source

here I would want to remove all the `Default`
<?xml version="1.0" encoding="UTF-8"?>
<Object>
  <DefaultID>XXXXXXX</DefaultID>
  <DefaultType>Random</DefaultType>
  <DefaultCustomer>
    <DefaultID>XXXXXXX</DefaultID>
    <DefaultName>John Doe</DefaultName>
    <DefaultAddress>33th Whitecaslt Blvd.</DefaultAddress>
    <DefaultNumber>+XX X XX XX XX XX</DefaultNumber>
  </DefaultCustomer>
</Object>

Expected Result

<?xml version="1.0" encoding="UTF-8"?>
<Object>
  <ID>XXXXXXX</ID>
  <Type>Random</Type>
  <Customer>
    <ID>XXXXXXX</ID>
    <Name>John Doe</tName>
    <Address>33th Whitecaslt Blvd.</Address>
    <Number>+XX X XX XX XX XX</Number>
  </Customer>
</Object>

My XSL

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" encoding="UTF-8" indent="yes"/>
    
    <xsl:template match="@*|node()">
        <xsl:if test="string-length(.)>0">
            <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
            </xsl:copy>
        </xsl:if>
    </xsl:template>

    <xsl:template match="//*[contains(name(), 'Default')]">
        <xsl:element name="{substring-after(name(), 'Default')}">
            <xsl:copy select=".">
                <xsl:apply-templates match="//*[contains(name(), 'Default')]" />
            </xsl:copy>
        </xsl:element> 
    </xsl:template>

</xsl:stylesheet>

What I Have

<?xml version="1.0" encoding="UTF-8"?>
<Object>
  <ID>XXXXXXX</ID>
  <Type>Random</Type>
  <Customer>
    <DefaultID>XXXXXXX</DefaultID>
    <DefaultName>John Doe</DefaultName>
    <DefaultAddress>33th Whitecaslte Blvd.</DefaultAddress>
    <DefaultNumber>+XX X XX XX XX XX</DefaultNumber>
  </Customer>
</Object>

as you can see the template doesn't match with the second node's level.

I suppose that my problem come from the scope of my XPath in my <xsl:copy select='.'> (that's appear to be useless btw) ? or maybe here, <xsl:copy> isn't the best choice (same result w/ <xsl:copy-of>)?


Solution

  • You are overthinking this. Try simply:

    <xsl:stylesheet version="1.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="*"/>
    
    <!-- identity transform -->
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    
    <xsl:template match="*[starts-with(name(), 'Default')]">
        <xsl:element name="{substring-after(name(), 'Default')}">
            <xsl:apply-templates select="@*|node()"/>
        </xsl:element> 
    </xsl:template>
    
    </xsl:stylesheet>