Excluding a single element in XSLT when using document() - xslt

I'm using document() to copy the content of an XML file (tagged for DITA) into a variable. Here is the code I had used previously that worked:
<xsl:variable name="fileContent">
<xsl:copy-of select="document($fileSrc)"/>
</xsl:variable>
Now, I want to include everything except the <draft-comment> element. I've tried to change document() as follows:
<xsl:variable name="fileContent">
<xsl:copy-of select="document($fileSrc)/*[not(draft-comment)]"/>
</xsl:variable>
This doesn't seem to work. The text is still there. Any suggestions on how I can fix this? Thank you for your help.

Create two templates with the mode attribute; one to copy nodes unchanged, and one to ignore draft-comment
<xsl:template match="#*|node()" mode="document">
<xsl:copy>
<xsl:apply-templates select="#*|node()" mode="document" />
</xsl:copy>
</xsl:template>
<xsl:template match="draft-comment" mode="document" />
Then you can use xsl:apply-templates instead of xsl:copy-of
<xsl:variable name="fileContent">
<xsl:apply-templates select="document($fileSrc)" mode="document" />
</xsl:variable>
Note that xsl:copy-of does a deep-copy, and your existing statement will copy the root element and everything under it just as long as it doesn't have a child node called draft-comment

Related

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.

XSLT identity transform with append and root element namespaces

I'm trying to create an XSLT library for the common task of passing through most of the content of XML data with small alterations.
The include file currently looks like this (pass-through.xslt):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- change default behaviour to copying all attributes and nodes -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!-- copy element but add a child node -->
<xsl:template name="append">
<xsl:param name="element"/>
<xsl:param name="appendage"/>
<xsl:element name="{name($element)}">
<xsl:copy-of select="$element/namespace::*"/>
<xsl:copy-of select="$element/#*"/>
<xsl:apply-templates select="$element/node()"/>
<xsl:copy-of select="$appendage"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
You could then create a stylesheet which included it and not have to worry repeating yourself over and over (calling stylesheet).
<xsl:stylesheet version="1.0"
xmlns:ns="http://example/namespace"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:include href="pass-through.xslt"/>
<!-- only worry about the transform you want -->
<xsl:template match="element-to-be-modified">
...
</xsl:template>
</xsl:stylesheet>
If you only want to add an element to the document then call "append".
<xsl:stylesheet
This works except for namespaces on an element I'm appending to. If I have an XML file with a namespace on the root element it chokes saying there is no namespace binding for the prefix. If I add the namespace of the root element to the library then it's happy but that kind of defeats the purpose of the library if you've got to go modifying it for every use.
I'd be happy adding the xmlns:ns="uri" to the calling stylesheet but the scope of the namespace declaration seems to only extend to that stylesheet - not to the included one where the "append" template is.
I'd like to be able to transform
<ns:root xmlns:ns="http://example/namespace">
<ns:child>old child</ns:child>
</ns:root>
To
<ns:root xmlns:ns="http://example/namespace">
<ns:child>old child</ns:child>
<ns:child>new child!</ns:child>
</ns:root>
Without having to include the identity transform boilerplate every time. I've tried an assortment of things, including adding a namespace="{namespace-uri()}" to the element in the "append" template but nothing seems to preserve the namespace prefix on the element appended to.
Replace
<xsl:element name="{name($element)}">
by
<xsl:element name="{name($element)}" namespace="{namespace-uri($element)}">
If, when you call your named template "append" you are already positioned on the element you are copying/modifying, then there really is no need to pass the current element as a parameter. You can just use xsl:copy here
<xsl:template name="append">
<xsl:param name="appendage"/>
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
<xsl:copy-of select="$appendage"/>
</xsl:copy>
</xsl:template>
In your example, you would just call it like this
<xsl:template match="ns:root">
<xsl:call-template name="append">
<xsl:with-param name="appendage"><ns:child>new child!</ns:child></xsl:with-param>
</xsl:call-template>
</xsl:template>
Using xsl:copy should copy your element and retain the namespace.

how to set Click event on Xslt output

This is my XSLT file:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:template match="/">
<xsl:for-each select="//child_4331">
<xsl:value-of select="*"/>
<xsl:value-of select="#value" />
<xsl:attribute name="onclick">
<xsl:call-template name="GetOnClickJavaScript" />
</xsl:attribute>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
How can I set the click event on child_4331 value?
You didn't say, but I'm assuming you want to copy the child_4331 element and add the onclick attribute.
I would get rid of the template matching '/' and create one to match 'child_4331'. Use xsl:copy to create a copy of the element and add the attribute inside it. If the child_4331 element has attributes or child elements you will want to use xsl:apply-templates to pick them up.
Here is a sample snippet. Your solution may vary depending on your desired output. I can't give you more without knowing what your source XML looks like and what you expect to see in the result.
<xsl:template match="child_4331">
<xsl:copy>
<xsl:attribute name="onclick">
<xsl:call-template name="GetOnClickJavaScript" />
</xsl:attribute>
</xsl:copy>
</xsl:template>

orbeon: applying xslt transform in xbl on bound document

In Orbeon Forms I need to create a component (using XBL) that when bound to an instance like
<OCinstructionitem>
<OCp>paragraph 1</OCp>
<OCp>paragraph 2 <OCem>with italics part</OCem> rest of paragraph 2 </OCp>
</OCinstructionitem>
creates an editable div like this:
<div contentEditable="true">
<p>paragraph 1</p>
<p>paragraph 2 <i>with italics part</i> rest of paragraph 2 </p>
</div>
My thought was that I need to do this using XSLT. I get this working when the to-be-transformed XML is inside the xforms document:
<oc:instructionitem>
<OCinstructionitem>
<!-- here the xml of above -->
...
</OCinstructionitem>
</oc:instructionitem>
but I want to let the XSLT operate on the bound node as in:
<oc:instructionitem ref="OCinstructionitem"/>
However, I canot access the bound node from the XSLT.
My question: is that just not possible? Or do I have to modify my XBL?
My XBL code:
<xbl:binding id="oc-instructionitem" element="oc|instructionitem">
<xbl:template xxbl:transform="oxf:xslt">
<xsl:transform version="2.0">
<xsl:template match="#*|node()" priority="-100">
<xsl:apply-templates select="#*|node()"/>
</xsl:template>
<xsl:template match="text()" priority="-100" mode="in-paragraph">
<xsl:copy>
<xsl:value-of select="."/>
</xsl:copy>
</xsl:template>
<xsl:template match="OCem" mode="in-paragraph">
<x:i><xsl:value-of select="."/></x:i>
</xsl:template>
<xsl:template match="OCp">
<x:div>
<xsl:apply-templates mode="in-paragraph"/>
</x:div>
</xsl:template>
<xsl:template match="/*">
<x:div contentEditable="true">
<xsl:apply-templates/>
</x:div>
</xsl:template>
</xsl:transform>
</xbl:template>
</xbl:binding>
</xbl:xbl>
Any help would be greatly appreciated,
edit: after helpfull comment of tohuwawohu (below)
It seems that you need to define a variable which is bound to the instance data. Like this:
<xsl:template match="oc:instructionitem">
<xforms:group xbl:attr="model context ref bind" xxbl:scope="outer">
<xxforms:variable name="binding" as="node()?" xxbl:scope="inner" >
<xxforms:sequence select="." xxbl:scope="outer"/>
</xxforms:variable>
<xforms:group xxbl:scope="inner">
<!-- Variable pointing to external single-node binding -->
<xforms:group ref="$binding">
<xsl:call-template name="main"/>
</xforms:group>
</xforms:group>
</xforms:group>
</xsl:template>
<xsl:template name="main">
<x:div contentEditable="true">
<xforms:repeat nodeset="*">
<xsl:call-template name="paragraph"/>
</xforms:repeat>
</x:div>
</xsl:template>
However, the XSLT elements still cannot act on the data. It can only generate XFORMS elements, which can act on the data. This means that something like <xsl:template match="OCp"> will never be selected. This is why the above code uses named templates.
So the question still stands: can the bound data be made available to the xslt code?
Martijn
I didn't test it, but i think it should be possible by breaking the encapsulation. Without this, your XBL will only have access to the content of the bound node, as in your third code snippet. If you want to make the XBL access the XForms instance data, you will have to declare a variable inside the XBL that's pointing to the single-node binding of the bound node.
Here's the code snippet from the Wiki example:
<xforms:group xbl:attr="model context ref bind" xxbl:scope="outer">
<xforms:group xxbl:scope="inner">
<!-- Variable pointing to external single-node binding -->
<xxforms:variable name="binding" as="node()?">
<xxforms:sequence select="." xxbl:scope="outer"/>
</xxforms:variable>
...
</xforms:group>
</xforms:group>
Having defined the single-node binding like this, it should be possible to reference that variable and use it for xslt transformation. Just replace the XSLT template element matching the root node and point it to the content of the variable $binding:
<xsl:transform version="2.0">
<xsl:template match="#*|node()" priority="-100">
<xsl:apply-templates select="#*|node()"/>
</xsl:template>
<xsl:template match="text()" priority="-100" mode="in-paragraph">
<xsl:copy>
<xsl:value-of select="."/>
</xsl:copy>
</xsl:template>
<xsl:template match="OCem" mode="in-paragraph">
<x:i><xsl:value-of select="."/></x:i>
</xsl:template>
<xsl:template match="OCp">
<x:div>
<xsl:apply-templates mode="in-paragraph"/>
</x:div>
</xsl:template>
<xsl:template match="oc:instructionitem">
<xforms:group ref="$binding">
<x:div contentEditable="true">
<xsl:apply-templates/>
</x:div>
</xforms:group>
</xsl:template>
Hope this works for you. Maybe this requires making provisions if the bound node (in your example: oc:instructionitem) isn't empty, so the xslt may process both that content as well as the content of the $binding variable.

How to remove <b/> from a document

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>