Reputation: 1238
I have an XML file which looks roughly like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Multiple xmlns:ns2="someNs2" xmlns="someGenericNs" xmlns:ns4="someNs4" xmlns:ns3="someNs3">
<Single>
<Id>60000</Id>
<Type>Activate</Type>
<Payload>
<ns3:Activation>
<ns3:Parent>
<ns3:TypeId>113</ns3:TypeId>
<ns3:TypeName>TestApplication</ns3:TypeName>
</ns3:Parent>
<ns3:Children>
<ns3:Child>
<ns3:Key>someKey</ns3:Key>
<ns3:ChildTypeName>BadAppType1</ns3:ChildTypeName>
</ns3:Child>
<ns3:Child>
<ns3:Key>someOtherKey</ns3:Key>
<ns3:ChildTypeName>GoodAppType1</ns3:ChildTypeName>
</ns3:Child>
</ns3:Children>
</ns3:Activation>
</Payload>
</Single>
</Multiple>
What I need to do is convert it into a simpler form, like this one:
<?xml version="1.0" encoding="UTF-8"?>
<MyNewRootNode xmlns="MyNewNamespace">
<Activation>
<ApplicationType>TestApplication</ApplicationType>
<ValidChildTypeName>GoodAppType1</ValidChildTypeName>
<ValidChildKey>someOtherKey</ValidChildKey>
</Activation>
</MyNewRootNode>
The initial XML can contain multiple children nodes, out of which only one will have a valid "CildTypeName" node. My issue is that if the XML does indeed contain multiple children nodes then my XSLT will just grab the first one and convert it to the new format even if it contains and invalid "ChildTypeName".
So to summarize: I need to convert my XML to a simpler form. To create this simpler form I need to filter through all the "Child" nodes and find the one which contains a valid "ChildTypeName" and copy the values of it's "Key" and "ChildTypeName" to the new XML.
I also have a predefined list of valid "ChildTypeNames". It contains about 3 names and that will not change any time soon.
This is the XSLT i've gotten to so far:
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="MyNewNamespace" xmlns:ns3="someNs3" exclude-result-prefixes="ns3">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:variable name="activationNode">
<xsl:text>Activation</xsl:text>
</xsl:variable>
<xsl:variable name="new-root-node">
<xsl:text>MyNewRootNode</xsl:text>
</xsl:variable>
<xsl:template match="/*">
<xsl:element name="{$new-root-node}">
<xsl:element name="{$activationNode}">
<xsl:call-template name="TemplateOne"/>
</xsl:element>
</xsl:element>
</xsl:template>
<xsl:template name="TemplateOne">
<xsl:element name="ApplicationType">
<xsl:value-of select="//ns3:Activation/ns3:Parent/ns3:TypeName"/>
</xsl:element>
<xsl:element name="ValidChildTypeName">
<xsl:value-of select="//ns3:Activation/ns3:Children/ns3:Child/ns3:ChildTypeName"/>
</xsl:element>
<xsl:element name="ValidChildKey">
<xsl:value-of select="//ns3:Activation/ns3:Children/ns3:Child/ns3:Key"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
But this will grab the first child node it finds, in this case the one with "BadAppType1".
UPDATE: Made some progress on this. Changed the last part of the XSLT to:
<xsl:for-each select="//ns3:Activation/ns3:Children/ns3:Child">
<xsl:if test="ns3:ChildTypeName[text()='GoodAppType1']">
<xsl:element name="ValidChildTypeName">
<xsl:value-of select="ns3:ChildTypeName"/>
</xsl:element>
<xsl:element name="ValidChildKey">
<xsl:value-of select="ns3:Key"/>
</xsl:element>
</xsl:if>
</xsl:for-each>
As far as I can tell I only need a way to check for more than one possible valid value and it should be right.
Upvotes: 2
Views: 1763
Reputation: 70648
IF you are trying to select Activation elements, who have a Child element with a ChildTypeName of "GoodAppType1" then maybe you can use template matching to select such an element where it exists.
<xsl:apply-templates
select="//ns3:Activation[ns3:Children/ns3:Child/ns3:ChildTypeName='GoodAppType1']" />
Then, within the template that matches this, you can easily output the ApplicationType
<ApplicationType><xsl:value-of select="ns3:Parent/ns3:TypeName" /></ApplicationType>
And then, select the Child element with the required ChildTypeName
<xsl:apply-templates select="ns3:Children/ns3:Child[ns3:ChildTypeName='GoodAppType1']" />
And within the template that matches Child you can likewise output the Key and ChildTypeName.
Try the following XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="MyNewNamespace" xmlns:ns3="someNs3" exclude-result-prefixes="ns3">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:variable name="activationNode">
<xsl:text>Activation</xsl:text>
</xsl:variable>
<xsl:variable name="new-root-node">
<xsl:text>MyNewRootNode</xsl:text>
</xsl:variable>
<xsl:template match="/">
<xsl:element name="{$new-root-node}">
<xsl:apply-templates select="//ns3:Activation[ns3:Children/ns3:Child/ns3:ChildTypeName='GoodAppType1']"/>
</xsl:element>
</xsl:template>
<xsl:template match="ns3:Activation">
<xsl:element name="{$activationNode}">
<ApplicationType>
<xsl:value-of select="ns3:Parent/ns3:TypeName"/>
</ApplicationType>
<xsl:apply-templates select="ns3:Children/ns3:Child[ns3:ChildTypeName='GoodAppType1']"/>
</xsl:element>
</xsl:template>
<xsl:template match="ns3:Child">
<ValidChildTypeName>
<xsl:value-of select="ns3:ChildTypeName"/>
</ValidChildTypeName>
<ValidChildKey>
<xsl:value-of select="ns3:Key"/>
</ValidChildKey>
</xsl:template>
</xsl:stylesheet>
When run against your input XML, the following is output
<MyNewRootNode xmlns="MyNewNamespace">
<Activation>
<ApplicationType>TestApplication</ApplicationType>
<ValidChildTypeName>GoodAppType1</ValidChildTypeName>
<ValidChildKey>someOtherKey</ValidChildKey>
</Activation>
</MyNewRootNode>
Upvotes: 1