Reputation: 393
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
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