I have an XSLT and i wanted to copy all the elements under a specific node with the exception of one particular node. The reason i am ignoring this node is, i need to check if node exists in xml & if it doesn't then i need to set a default value. Here is the code snipette i have & it doesn't seem to work
I have couple other templates in XSLT & i included mode so i can specifically use this for a particular node
<xsl:template match="*" mode="copyexcludingDL">
<xsl:copy>
<xsl:apply-templates select="#*|node()[not(self::DriversLicense)]"/>
</xsl:copy>
</xsl:template>
Here is the Node & the logic
<xsl:for-each select="Vehicle">
<xsl:apply-templates mode="copyexcludingDL" select=".">
<xsl:choose>
<xsl:when test="DriversLicense">
<xsl:apply-templates mode="copy" select="DriversLicense" />
</xsl:when>
<xsl:otherwise>
<xsl:element name="DriversLicense">
<xsl:text>None</xsl:text>
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</xsl:apply-templates>
</xsl:for-each>
It is probably "not working" because you cannot have an xsl:choose as a child of xsl:apply-templates.
If your logic is that you want copy the DriversLicense node if it exists, but add a default value if it doesn't, then you can achieve by template matching Vehicle nodes which have no DriversLicense node.
To do this, instead of doing <xsl:for-each select="Vehicle">, replace the whole block with an xsl:apply-templates instead...
<xsl:apply-templates select="Vehicle" />
Then add a template that matches Vehicle nodes without a DriversLicense node, that copies the node and adds a default
<xsl:template match="Vehicle[not(DriversLicense)]">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
<DriversLicense>None</DriversLicense>
</xsl:copy>
</xsl:template>
This assumes you are also using the identity template as well in your XSLT
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
Related
I get a warning
ambiguous rule match
from the processor for the templates copyReference and the identity transform.
<xsl:template name="processChildNodes">
<xsl:param name="El"/>
<xsl:for-each select="$El/node()">
<xsl:choose>
<xsl:when test="#sameas">
<xsl:apply-templates mode="copyReference" select="id(substring-after(#sameas, '#'))"/>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="." />
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
<xsl:template match="*" mode="copyReference" name="copyReference">
<xsl:copy>
<xsl:apply-templates select="#* except (#stem.dir, #stem.sameas)"/>
</xsl:copy>
</xsl:template>
<xsl:template match="node() | #*" mode="#all">
<xsl:copy>
<xsl:apply-templates select="node() | #*"/>
</xsl:copy>
</xsl:template>
Here is an xml snippet:
<layer>
<note oct="3" pname="b" stem.dir="up" stem.sameas="#note_17544b" xml:id="note_17544"/>
</layer>
<layer>
<note oct="4" pname="d" xml:id="note_17592"/>
<note sameas="#note_17544" xml:id="note_17544b"/>
</layer>
What I want to do is just to copy the node which is referenced from the #sameas-attribute without #stem.dir and #stem.sameas. There could be different nodes local-names() on which the will be applied on. So I'd rather not specify the node names in the #match-attribute of the copyReference template. I thought if I pass the nodes I need with #select-attribute and also add #mode it will match only what I need. And actually it works, but as I'm getting the warning something should be wrong.
node() is short-hand for *|text()|comment()|processing-instruction() and so because the identity template has mode="#all" on it, it will match any element with the same priority as the "copyReference" template when the "copyReference" mode is used.
The solution depends on what else your stylesheet does, but there are a number of possibilities
Remove mode="#all" from the identity template (this would only work if there were not other modes in your XSLT)
Add priority="2" to your "copyReference" template, so that when the mode "copyReference" was used, your specific template would get priority.
Change <xsl:apply-templates mode="copyReference"... to be an xsl:for-each instead and do away with the template match.
Change the "copyReference" template to explicitly match "note" rather than "*" as this would then give it a higher priority (but this obviously assumes you would only ever need to match note elements)
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.
I have to make uppercase of all node values inside a parent node while copying the whole section.
For Example:
ATPM/37 zATP - Miscellaneous Exceptions Blank text box without number 1 Said
Change to:
ATPM/37 ZATP - MISCELLANEOUS EXCEPTIONS BLANK TEXT BOX WITHOUT NUMBER 1 SAID
In XSLT 2.0, try adding
<xsl:template match="text()">
<xsl:value-of select="upper-case(.)"/>
</xsl:template>
In XSLT 1.0, use
<xsl:template match="text()">
<xsl:value-of select="translate(., $smallcase, $uppercase)" />
</xsl:template>
<xsl:variable name="smallcase" select="'abcdefghijklmnopqrstuvwxyz'" />
<xsl:variable name="uppercase" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'" />
instead
The xsl:copy-of instruction always does an exact copy; you can't use it to make a copy-with-changes. For that, use the identity template to copy things that you want to copy exactly, and a different template for things that you want to change. Thus:
<xsl:template match="*">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()">
<xsl:value-of select="upper-case(.)"/>
</xsl:template>
Are there any XSLT statements that will execute in consideration of other XSLT statements within the same stylesheet?
For example, if I have two copy statements matched to the same node (but only desire one copied node that contains the modifications declared in BOTH copy statements) is there a statement that will do this?
Assume that I cannot put all the transformations in one copy node, but instead have to use two or more.
---Clearer example---
//XML
<toy></toy>
//XSLT
<xsl:template match="toy">
<xsl:copy>
<xsl:attribute name="label">SOME TOY</xsl:attribute>
</xsl:copy>
</xsl:template>
<xsl:template match="toy">
<xsl:copy>
<xsl:apply-templates select="#*" />
<xsl:element name="range">
<xsl:element name="min">200001</xsl:element>
<xsl:element name="max">999999</xsl:element>
</xsl:element>
</xsl:copy>
</xsl:template>
My desired result would be a new toy node that is copied to a new file that has both things applied to it, so something like:
<toy label='SOME TOY'>
<range>
<min>200001</min>
<max>999999</max>
</range>
</toy>
Not two different copies
Is this possible? Is there some way I can redo the first template so that will make this one outcome?
There is rule in XSLT specification, which forbid this - Conflict Resolution for Template Rules.
If node fits several templates - only one template will be executed - in relation to the template import precedence, priority or document order, etc.
But you can separate it with named templates:
<xsl:template match="toy">
<xsl:call-template name="toyAttribute" />
<xsl:call-template name="toyElements" />
</xsl:template>
<xsl:template name="toyAttribute">
<xsl:copy>
<xsl:attribute name="label">SOME TOY</xsl:attribute>
</xsl:copy>
</xsl:template>
<xsl:template name="toyElements">
<xsl:copy>
<xsl:apply-templates select="#*" />
<xsl:element name="range">
<xsl:element name="min">200001</xsl:element>
<xsl:element name="max">999999</xsl:element>
</xsl:element>
</xsl:copy>
</xsl:template>
Update:
If you asking about only updating <toy> node with attribute and elements you don't need separate templates:
<!--toy template -->
<xsl:template match="/toys/toy">
<!--copy toy node with namespaces -->
<xsl:copy>
<!-- copy toy node attributes -->
<xsl:apply-templates select="#*" />
<!-- add new attribute or xsl:call-template name="toyAttribute"-->
<xsl:attribute name="label">SOME TOY</xsl:attribute>
<!-- copy toy node child elements -->
<xsl:apply-templates select="node()" />
<!-- add new elements - or xsl:call-template name="toyElements"-->
<xsl:element name="range">
<xsl:element name="min">200001</xsl:element>
<xsl:element name="max">999999</xsl:element>
</xsl:element>
</xsl:copy>
</xsl:template>
<!--Copy node content -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
which for XML:
<?xml version="1.0" encoding="UTF-8"?>
<toys>
<toy name="a">
<toy-part/>
</toy>
<toy name="b">
<toy-part/>
</toy>
</toys>
will give following result:
<?xml version="1.0" encoding="utf-8"?><toys>
<toy name="a" label="SOME TOY">
<toy-part/>
<range>
<min>200001</min>
<max>999999</max>
</range>
</toy>
<toy name="b" label="SOME TOY">
<toy-part/>
<range>
<min>200001</min>
<max>999999</max>
</range>
</toy>
</toys>
I'm trying to have an XSLT that copies most of the tags but removes empty "<b/>" tags. That is, it should copy as-is "<b> </b>" or "<b>toto</b>" but completely remove "<b/>".
I think the template would look like :
<xsl:template match="b">
<xsl:if test=".hasChildren()">
<xsl:element name="b">
<xsl:apply-templates/>
</xsl:element>
</xsl:if>
</xsl:template>
But of course, the "hasChildren()" part doesn't exist ... Any idea ?
dsteinweg put me on the right track ... I ended up doing :
<xsl:template match="b">
<xsl:if test="./* or ./text()">
<xsl:element name="b">
<xsl:apply-templates/>
</xsl:element>
</xsl:if>
</xsl:template>
This transformation ignores any <b> elements that do not have any node child. A node in this context means an element, text, comment or processing instruction node.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="b[not(node()]"/>
</xsl:stylesheet>
Notice that here we use one of the most fundamental XSLT design patterns -- using the identity transform and overriding it for specific nodes.
The overriding template will be selected only for nodes that are elements named "b" and do not have (any nodes as) children. This template is empty (does not have any contents), so the effect of its application is that the matching node is ignored/discarded and is not reproduced in the output.
This technique is very powerful and is widely used for such tasks and also for renaming, changing the contents or attributes, adding children or siblings to any specific node that can be matched (avery type of node with the exception of a namespace node can be used as a match pattern in the "match" attribute of <xsl:template/>
Hope this helped.
Cheers,
Dimitre Novatchev
I wonder if this will work?
<xsl:template match="b">
<xsl:if test="b/text()">
...
See if this will work.
<xsl:template match="b">
<xsl:if test=".!=''">
<xsl:element name="b">
<xsl:apply-templates/>
</xsl:element>
</xsl:if>
</xsl:template>
An alternative would be to do the following:
<xsl:template match="b[not(text())]" />
<xsl:template match="b">
<b>
<xsl:apply-templates/>
</b>
</xsl:template>
You could put all the logic in the predicate, and set up a template to match only what you want and delete it:
<xsl:template match="b[not(node())] />
This assumes that you have an identity template later on in the transform, which it sounds like you do. That will automatically copy any "b" tags with content, which is what you want:
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
Edit: Now uses node() like Dimitri, below.
If you have access to update the original XML, you could try using use xml:space=preserve on the root element
<html xml:space="preserve">
...
</html>
This way, the space in the empty <b> </b> tag is preserved, and so can be distinguished from <b /> in the XSLT.
<xsl:template match="b">
<xsl:if test="text() != ''">
....
</xsl:if>
</xsl:template>