Search code examples
xmlxsltxpathxslt-1.0xslt-2.0

copy node where certain attributes are present (or not present)


I currently have this xsl that is working great

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="node()">
        <xsl:copy copy-namespaces="no">
            <xsl:copy-of select="@Ccy"/>
            <xsl:apply-templates />
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

This essentially re-writes my XML removing all attributes except Ccy. However, I now need to include attributes with the name "name" as well. Id like to either union the attribute names that I would like to keep:

<xsl:copy-of select="@Ccy | @name"/>

Or, ideally, copy all attributes EXCEPT

<xsl:copy-of select="!@BadAttyName"/>

Any ideas??


Solution

  • You can use the following stylesheets:

    INPUT:

    $more input.xml 
    <?xml version="1.0"?>
    <a>
      <b Ccy="123" name="test1" BadAttyNameTest="toRemove1" BadAttyNameTestt="toRemovee1" other="hey1">toto</b>
      <b Ccy="456" name="test2" BadAttyNameTest="toRemove2" BadAttyNameTestt="toRemovee2" other="hey2">abc</b>
      <b Ccy="789" name="test3" BadAttyNameTest="toRemove3" BadAttyNameTestt="toRemovee3" other="hey3">uvw</b>
    </a>
    

    UNION:

    ::::::::::::::
    inputUnion.xsl
    ::::::::::::::
    <?xml version="1.0" encoding="utf-8"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:output omit-xml-declaration="yes" indent="yes"/>
        <xsl:strip-space elements="*"/>
    
        <xsl:template match="node()">
            <xsl:copy copy-namespaces="no">
                <xsl:copy-of select="@Ccy | @name"/>
                <xsl:apply-templates />
            </xsl:copy>
        </xsl:template>
    </xsl:stylesheet>
    

    OUTPUT UNION:

    $xsltproc inputUnion.xsl input.xml 
    <a>
      <b Ccy="123" name="test1">toto</b>
      <b Ccy="456" name="test2">abc</b>
      <b Ccy="789" name="test3">uvw</b>
    </a>
    

    It will only copy the union of the attributes @Ccy | @name, the other attributes are not taken into account.

    EXCEPT:

    ::::::::::::::
    inputNOT.xsl
    ::::::::::::::
    <?xml version="1.0" encoding="utf-8"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:output omit-xml-declaration="yes" indent="yes"/>
        <xsl:strip-space elements="*"/>
    
        <xsl:template match="node()">
            <xsl:copy copy-namespaces="no">
                <xsl:copy-of select="@*[not(starts-with(name(),'BadAttyName'))]"/>
                <xsl:apply-templates />
            </xsl:copy>
        </xsl:template>
    </xsl:stylesheet>
    

    OUTPUT EXCEPT:

    $xsltproc inputNOT.xsl input.xml 
    <a>
      <b Ccy="123" name="test1" other="hey1">toto</b>
      <b Ccy="456" name="test2" other="hey2">abc</b>
      <b Ccy="789" name="test3" other="hey3">uvw</b>
    </a>
    

    the syntax @*[not(starts-with(name(),'BadAttyName'))] will take all attributes that satisfies the condition in the brackets. The condition is all elements that do not start with BadAttyName, this is created by combining not() and starts-with().