echobravo
echobravo

Reputation: 393

XSLT selective copy

I am making my first foray into XML transforming and I am looking to use XSLT for performing XML -> XML transformations and I have tried several ways (unsuccessfully) to filter nodes based on specific attributes...

source xml file like looks like this:

<?xml version="1.0" encoding="UTF-8" ?>
    <resource name="zoo">   
     <resource name="foo">
          <customResource name="bar">
           <value name="Zanibar"/>
           <value name="Echo"/>
           <value name="Blah"/>
          </customResource>
        </resource>

       <resource name="otherfoo">
          <customResource name="otherbar">
           <value name="crowbar"/>
           <value name="motar"/>
           <value name="brick"/>
          </customResource>
        </resource>

     </resource>

And my xsl looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>

    <!-- this template sets match for all nodes at root level and iterates and makes a copy subject to templates
        if no other templates are defined this will make an exact copy -->
    <xsl:template match="*">
        <xsl:copy>
            <xsl:for-each select="@*">
                <xsl:copy>
                    <xsl:apply-templates/>
                </xsl:copy>
            </xsl:for-each>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template> 

    <!-- find foo/bar and rebuild with only desired values -->
    <xsl:template match="//foo/bar/">
        <xsl:copy-of select="//value[@name='Echo']" />
    </xsl:template>
    </xsl:template>
</xsl:stylesheet>

And I am looking to get a filtered result that looks like this:

<?xml version="1.0" encoding="UTF-8" ?>
    <resource name="zoo">   
     <resource name="foo">
          <customResource name="bar">
           <value name="Echo"/>
          </customResource>
        </resource>

       <resource name="otherfoo">
          <customResource name="otherbar">
           <value name="crowbar"/>
           <value name="motar"/>
           <value name="brick"/>
          </customResource>
        </resource>
     </resource>

Only I end up with a full copy of the original xml and not my filtered version.

Thanks in advance for any advice! EB

The results in this template are similar to a previous attempt I made; i.e. it does the inclusive filtering, but drops the attribute on the parent node

<resource name="foo">
          <customResource> <!-- should still have name="bar" -->
           <value name="Echo"/>
          </customResource>
        </resource>

Latest results are very close to the transform I have been looking for, in one case the formatting was removed, but not in the other... (I'm using xsltproc on linux )

<resource name="foo">
          <customResource name="bar"><value name="Echo"/></customResource>
        </resource>

But that part is not as critical as dealing with duplicate custom resources with the same name attribute, my apologies I should have mentioned that before. e.g.

<?xml version="1.0" encoding="UTF-8" ?>
    <resource name="zoo">  
     <resource name="nested"> 
     <resource name="foo">
          <customResource name="bar">
           <value name="Zanibar"/>
           <value name="Echo"/>
           <value name="Blah"/>
          </customResource>
        </resource>
       </resource>
       <resource name="foo">
          <customResource name="otherbar">
           <value name="crowbar"/>
           <value name="motar"/>
           <value name="brick"/>
          </customResource>
        </resource>
     </resource>

The template filtering for "foo" emptied the customResources under duplicate resource... Apologies again, I goofed up when I translated into foobar land :-) I was able to filter for the various "foo"s and switch between the customResources "bar", "otherbar", etc... changed to use copy instead of

Upvotes: 1

Views: 1928

Answers (1)

michael.hor257k
michael.hor257k

Reputation: 116957

Here's one way to look at it:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:strip-space elements="*"/>

<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>

<!-- your basic identity transform -->
<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>

<!-- exception -->
<xsl:template match="value[@name!='Echo']"/>

</xsl:stylesheet>

See also:
http://en.wikipedia.org/wiki/Identity_transform


EDIT

Here's a different approach that allows you to pick the values to be included for each customResource:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:strip-space elements="*"/>
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>

<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="customResource">
    <xsl:choose>
        <xsl:when test="@name='bar'">
            <xsl:copy>  
                <xsl:apply-templates select="@*"/>
                <xsl:apply-templates select="value[@name='Echo']"/>
            </xsl:copy>
        </xsl:when>

        <xsl:when test="@name='otherbar'">
            <xsl:copy>  
                <xsl:apply-templates select="@*"/>
                <xsl:apply-templates select="value[@name='crowbar']"/>
            </xsl:copy>
        </xsl:when>
        <!-- continue for other customResources  -->
    </xsl:choose>   
</xsl:template>

</xsl:stylesheet>

EDIT 2

If you prefer, you can select the customResource by the name of its parent, like this:

<xsl:when test="parent::resource/@name='foo'">

If that's not specific enough (i.e. if the parent resource can have more than one customResource), then test for both names

In any case, I believe you have enough there to finish this on your own.

Upvotes: 3

Related Questions