What is the default select of XSLT apply-templates? - xslt

The identity template looks like this:
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
Does <xsl:apply-templates select="#*|node()" /> select more than <xsl:apply-templates />, or could the identity template have been like this?
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
What exactly is selected when I do the following?
<xsl:apply-templates />

Does <xsl:apply-templates select="#*|node()" /> select more than
<xsl:apply-templates />, or could the identity template have been
like this?
<xsl:apply-templates/>
is equivalent to:
<xsl:apply-templates select="node()"/>
and this is a shorter former of:
<xsl:apply-templates select="child::node()"/>
and this is a equivalent to:
<xsl:apply-templates select="* | text() | comment() | processing-instruction()"/>
As we see from the last instruction, the xsl:apply-templates instruction you are asking about, doesn't select any attributes, therefore it cannot be used as a shorthand for:
<xsl:apply-templates select="#*|node()"/>

The default select for <xsl:apply-templates/> is just "node()", it doesn't include attributes.

The default selection of apply-templates is node(), which is shorthand for child::node(). This XPath expressions is evaluated as follows:
First, all nodes from the "child" axis are taken. This are all the direct children of the current element, i.e. other elements, text, and comments, but not the attributes.
Then this node set is filtered with the node test "node()". In this case, no elements are filtered because that test matches everything.
So with <xsl:apply-templates />, templates for the child elements are applied but not for the attributes. In case of the copy template this would mean that the attributes are not copied.

Related

XSLT copy excluding attributes

I've got an XSLT that is translating from XML A to XML B and the 2 are 95% the same except for the fact XML B has fewer attributes. So, for example, I need to translate:
<PaymentDetail PaymentType="CC" ItemNbr="6" CcardNbr="9999999999999999" CcardExp="0523" CcardVend="AA" PayRmrk="" Description="Advance Payment" Total="999.99" />
to
<PaymentDetail PaymentType="CC" ItemNbr="6" CcardNbr="9999999999999999" CcardExp="0523" CcardVend="AA" Total="999.99" />
I've tried copy with an attribute list, but that seems to be long-winded, the same as just doing element = PaymentDetail, attribute name=..... & basically "hard-coding" the whole thing.
I'm using version 1
The standard solution uses template rules:
<!-- Default template: copy nodes unchanged -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!-- Drop selected attributes -->
<xsl:template match="#PayRmrk | #Description"/>
You could exclude the attributes you don't need in your Xpath statement :
<xsl:template match="PaymentDetail">
<xsl:copy>
<xsl:copy-of select="#* except (#PayRmrk, #Description)"/>
</xsl:copy>
</xsl:template>
<!-- Identity template -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
See it working here : http://xsltfiddle.liberty-development.net/6pS26mv

XSLT: create node if does not exists

I know similar questions are already there but none of them seem to work for me.
So shortly, I have XML file with tag "Lokal" that in most cases does not appear but it should. Not making things easier: I also need to change a name of "Lokal" to let's say "Lokal_test". My goal is modify node name(if exists) or create it and rename (if does not exists).
Data from XML will be imported to MS Access data so they need to match perfectly with table...
Sample XML:
<Dane>
<InformacjeOWpisie>
<DaneAdresowe>
<AdresGlownegoMiejscaWykonywaniaDzialalnosci>
<Budynek>3a</Budynek>
<Wojewodztwo>podlaskie</Wojewodztwo>
</AdresGlownegoMiejscaWykonywaniaDzialalnosci>
</DaneAdresowe>
</InformacjeOWpisie>
<InformacjeOWpisie>
<DaneAdresowe>
<AdresGlownegoMiejscaWykonywaniaDzialalnosci>
<Budynek>8r</Budynek>
<Lokal>2</Lokal>
<Wojewodztwo>mazowieckie</Wojewodztwo>
</AdresGlownegoMiejscaWykonywaniaDzialalnosci>
</DaneAdresowe>
</InformacjeOWpisie>
</Dane>
Desired output:
<Dane>
<InformacjeOWpisie>
<DaneAdresowe>
<AdresGlownegoMiejscaWykonywaniaDzialalnosci>
<Budynek>3a</Budynek>
<Lokal_test/>
<Wojewodztwo>podlaskie</Wojewodztwo>
</AdresGlownegoMiejscaWykonywaniaDzialalnosci>
</DaneAdresowe>
</InformacjeOWpisie>
<InformacjeOWpisie>
<DaneAdresowe>
<AdresGlownegoMiejscaWykonywaniaDzialalnosci>
<Budynek>8r</Budynek>
<Lokal_test>2</Lokal_test>
<Wojewodztwo>mazowieckie</Wojewodztwo>
</AdresGlownegoMiejscaWykonywaniaDzialalnosci>
</DaneAdresowe>
</InformacjeOWpisie>
</Dane>
This question(XSLT: create node if not exists seemed to be the awnser to my problems but when trying to use it does not work.
Not sure why?
<xsl:template match="InformacjeOWpisie/DaneAdresowe/AdresGlownegoMiejscaWykonywaniaDzialalnosci/Lokal">
<Lokal_test>
<xsl:apply-templates select="#*|node()" />
</Lokal_test>
</xsl:template>
EDIT:
When I get rid of parent Lokal_test dissapears. I use below code to say "bye bye" to parent:
<xsl:template match="InformacjeOWpisie/DaneAdresowe/AdresGlownegoMiejscaWykonywaniaDzialalnosci">
<xsl:apply-templates select="#*|node()" />
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="AdresGlownegoMiejscaWykonywaniaDzialalnosci/Budynek">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
<xsl:choose>
<xsl:when test="exists(following-sibling::Lokal)">
<Lokal_test>
<xsl:value-of select="following-sibling::Lokal"/>
</Lokal_test>
</xsl:when>
<xsl:when test="not(following-sibling::Lokal)">
<xsl:element name="Lokal_test"/>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template match="Lokal"/>
You approach was right, but incomplete. You only created the new Local_test element.
So try these two templates in combination with the indentity template:
<!-- Handles the replacement of the 'Lokal' element -->
<xsl:template match="AdresGlownegoMiejscaWykonywaniaDzialalnosci/Lokal">
<Lokal_test>
<xsl:apply-templates select="node()|#*" />
</Lokal_test>
</xsl:template>
<!-- Creates a new 'Lokal_test' element if no 'Lokal' element exists -->
<xsl:template match="AdresGlownegoMiejscaWykonywaniaDzialalnosci[not(Lokal)]">
<xsl:copy>
<xsl:apply-templates select="node()/following-sibling::Wojewodztwo/preceding-sibling::*|#*" /> <!-- Copy nodes before 'Wojewodztwo' -->
<Lokal_test />
<xsl:apply-templates select="Wojewodztwo|Wojewodztwo/following-sibling::*|#*" /> <!-- Copy nodes after 'Wojewodztwo' (including) -->
</xsl:copy>
</xsl:template>
The second template puts the Lokal_test element before the Wojewodztwo element and copies the surrounding nodes.

Keep schemaversion while doing <xsl:apply-templates select="#*|node()"/>

I have requirements to copy whole node including schemaversion and namespaces. (Below is part of XML)
<ns0:Body>
<std:test com:schemaVersion = "4.1" xmlns:com = "http://www.test.com/DI/D2P/Broker/Schemas/CommonTypes" xmlns:std = "http://www.test1.com/DI/D2P/Broker/Schemas/test" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance">
<com:SystemMessageHeader>
...
...
I am using below xslt to do this task. (i have mentioned only needed portion of XSLT.) With this XSLT, it does copy of whole node including namespaces but it does not copy com:schemaVersion = "4.1". Please help me how can i fix it ?
<nos:Body>
<xsl:for-each select ="nos:GRRCall/nos:Response/ns0:Body/*">
<xsl:copy>
<xsl:variable name="v1" select="position()"/>
<xsl:variable name="v2" select="count(../../../../*)"/>
<xsl:apply-templates />
</xsl:copy>
</xsl:for-each>
</nos:Body>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
<xsl:apply-templates select="com:SystemMessageHeader"/>
</xsl:copy>
</xsl:template>
When you perform an apply-templates without specifying a select expression -- as in your stylesheet fragment -- the attributes of the context node are not selected for transformation. That's why the canonical identity transform explicitly selects attributes:
<!-- Identity transform -->
<xsl:template match="node()|#*">
<xsl:copy>
<!-- HERE: -->
<xsl:apply-templates select="node()|#* />
</xsl:copy>
</xsl:template>
Your first alternative, then, would be to follow the model of the identity transform when you apply templates to the children of <ns0:Body> elements.
Your second alternative would be to explicitly transform the wanted attribute by adding ...
<xsl:apply-templates select="#com:schemaVersion" />
... which will not affect other attributes, and will do nothing if the source element has no such attribute.
A third alternative would be to ensure that the transformed elements have the wanted attribute, even if the source elements didn't, by inserting code to create one into the template:
<xsl:attribute name="com:schemaVersion">
<xsl:value-of select="#com:schemaVersion" />
</xsl:attribute>
That particular version will provide an empty string as the attribute value when the source node has no such attribute; you could modify it to provide a default value instead if desired.

How can I execute an apply-templates on all non-selected elements within a xsl:copy?

Let's say I have the following XSLT:
<xsl:template match="DTS:Executable[#DTS:ExecutableType='SSIS.Package.2' or #DTS:ExecutableType='MSDTS.Package.1']">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:apply-templates select="./DTS:Property"/>
<xsl:apply-templates select="./DTS:ConnectionManager"/>
<xsl:apply-templates select="./DTS:Configuration"/>
<xsl:apply-templates select="./DTS:LogProvider"/>
</xsl:copy>
</xsl:template>
I realize that if there are other node types, such as DTS:Variable, they'll be filtered from the produced XML since no apply-templates statement selects them.
My question is: how can I conclude the copy with an apply-templates statement that selects all elements not specified in the previous apply-templates statements?
I've tried something like:
<xsl:apply-templates select="./node()[name!='DTS:Property' and name!='DTS:ConnectionManager' and name!='DTS:Configuration' and name!='LogProvider']" />
but that doesn't seem to work.
You should be using the function name() and not just name in your expression. Also note the ./ is superfluous here, so you can just write this.....
<xsl:apply-templates select="node()[name()!='DTS:Property' and name()!='DTS:ConnectionManager' and name()!='DTS:Configuration' and name()!='LogProvider']" />
Alternatively, it could be shorter to write the following
<xsl:apply-templates select="node()[not(self::DTS:Property|self::DTS:ConnectionManager|self::DTS:Configuration|self::LogProvider)]" />

XSLT copy-of how I can skip the child node while copying through XSL:copy-of

Question for Copy
input:
<Rel>
<IRel UID1="3a4d1d2909d0" UID2="35fe61082294" DefUID="AssetSupplier" />
<IObject UID="3a4d1d2909d0.AssetSupplier.35fe61082294" />
<SPXSupplier>
<ISPFOrganization />
<ISPFAdminItem />
<IObject UID="b73ebb87-cd36-4c25-b9ed-35fe61082294"
Description="local supplier made in form (10C)"
Name="CASTROL1200" />
<ISupplierOrganization />
</SPXSupplier>
</Rel>
Output:
I only want to skip SPXSupplier and its child node in my output
<Rel>
<IRel UID1="3a4d1d2909d0" UID2="35fe61082294" DefUID="AssetSupplier" />
<IObject UID="3a4d1d2909d0.AssetSupplier.35fe61082294" />
</Rel>
currently I am using this copy which copy all the things including the child,
<xsl:copy-of select="self::node()"/>
I only want <Rel>, <IRel> and <IObject> tags. excluding other stuff.
Here's a refinement of Alex's answer.
<xsl:template match="SPXSupplier"/>
<xsl:template match="*">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
The empty template for SPXSupplier means that when you hit one of these elements, the subtree below that element is not processed. I've also used a version of the identity template that copies attributes unconditionally, which is more efficient.
xsl:copy-of copies the whole subree. To exclude an SPXSupplier element you can use the following approach:
<xsl:template match="//Rel">
<xsl:copy>
<xsl:apply-templates select="#*|IRel|IObject"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>