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.
Related
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
I have a .xsl file like this:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:exslt="http://exslt.org/common">
<xsl:template match="/>
<fo:root>
<fo:block>...</fo:block>
</fo:root>
</xsl:template>
</xsl:stylesheet>
How can I use templates to match and style the generated fo elements? For example, if I want to give my fo:table-cells red backgrounds, I'd like to be able to do
<xsl:template match="fo:table-cell">
<xsl:attribute name="background-color">red</xsl:attribute>
</xsl:template>
I found this and then tried something along the lines of
<xsl:template match="/>
<xsl:variable name="foRoot">
<fo:root>
<fo:block>...</fo:block>
</fo:root>
</xsl:variable>
<xsl:apply-templates select="exslt:node-set($foRoot)" />
</xsl:template>
but this results in a stack overflow due to endless recursion. When I try to avoid this, for example by doing
<xsl:apply-templates select="exslt:node-set($foRoot)/*" />
I get an empty document. When trying to fix that by adding
<xsl:copy-of select="$foRoot" />
right after, I don't get any errors but the table-cells still have a default white background.
If you really use an XSLT 2 processor then first of all you don't need exsl:node-set.
As for your template
<xsl:template match="fo:table-cell">
<xsl:attribute name="background-color">red</xsl:attribute>
</xsl:template>
that would match a FO table-cell but transform it into an attribute. So you rather want
<xsl:template match="fo:table-cell">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:attribute name="background-color">red</xsl:attribute>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
as that adds the attribute to a shallow copy of the element and then keeps processing alive for child elements with apply-templates.
Of course you will also need to add the identity transformation template
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
to make sure the elements you don't want to change are copied through. It might be necessary to use modes to separate processing steps if the other templates you have interfere with the identity transformation.
This question already has answers here:
XSLT with XML source that has a default namespace set to xmlns
(3 answers)
Closed 4 years ago.
I am new to XSLT transformation. I have namepace mapping issue in my output xml.
The input XML is
<m:class xmlns:m="http://www.NotRequirednamespace.com">
<queryDetails>hello</queryDetails>
</m:class>
My XSLT is look like
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xpath-default-namespace="http://www.neededNamespace.com" xmlns:t="http://www.NotRequirednamespace.com" exclude-result-prefixes="t">
<xsl:output indent="yes" method="xml" encoding="utf-8" omit-xml-declaration="yes"/>
<!-- Stylesheet to remove all namespaces from a document -->
<!-- NOTE: this will lead to attribute name clash, if an element contains
two attributes with same local name but different namespace prefix -->
<!-- Nodes that cannot have a namespace are copied as such -->
<xsl:template match="/">
<school xmlns="http://www.neededNamespace.com">
<xsl:apply-templates/>
</school>
</xsl:template>
<!-- template to copy elements -->
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="#* | node()"/>
</xsl:element>
</xsl:template>
<!-- template to copy attributes -->
<xsl:template match="#*">
<xsl:attribute name="{local-name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
<!-- template to copy the rest of the nodes -->
<xsl:template match="comment() | text() | processing-instruction()">
<xsl:copy/>
</xsl:template>
</xsl:stylesheet>
The output XML is
<school xmlns="http://www.neededNamespace.com">
<class xmlns="">
<queryDetails>hello</queryDetails>
</class>
</school>
But I dont want the name default namespace (xmlns="") in the element class. I like to specify the default namespace for the class element as "http://www.neededNamespace.com". So that I need the output xml as follows.
<school xmlns="http://www.neededNamespace.com">
<class>
<queryDetails>hello</queryDetails>
</class>
</school>
I have tried all the options i know. Can you help in this. Thanks in advance.
Do you really need all that code? Or are you just using this as an incantation, in the hope it will somehow appease the evil spirits? Like what does xpath-default-namespace do in an XSLT 1.0 stylesheet? (Answer: either nothing, or produce a fatal error - depending on how tolerant your processor is).
Now, if your XML example is representative, then all you need to do is:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.neededNamespace.com">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<school>
<xsl:apply-templates/>
</school>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
What this does is:
Create a new root element named <school>;
Create a new element for each existing element, and name it with the local name of the existing element.
Since the stylesheet contains a declaration of a default namespace, ALL newly created elements (in both #1 and #2 above) will be placed in that namespace.
Since your input does not include any attributes, that should be all the code that's required.
In case you worry they may add some attributes in the future that you wish to pass to the output, just change the second template to:
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
This assumes the attributes will be in no namespace - and that's a very reasonable assumption. Attributes do not inherit their parent's namespace, and very rarely do you see authors place attributes in namespaces explicitly. Only if you really need to anticipate such possibility do you need additional code to handle the attributes.
So you don't want to remove the namespace of the "class" element, you want to change it. Use the "namespace" attribute of the xsl:element node.
Instead of
<xsl:element name="{local-name()}">
you want
<xsl:element name="{local-name()}" namespace="http://www.neededNamespace.com">
I am trying to transform an xml file that contain a list of words and I am trying to exclude some elements from the resulting document, more concretely and
My List is as follows:
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="merge.xsl"?>
<dic:englishWords xmlns:dic = "dictionary">
<dic:words>
<dic:englishWords xmlns:dic = "dictionary">
<dic:title>
English Dictionary
</dic:title>
<dic:author>
<dic:authorsName>
Author:
<dic:name>
User
</dic:name>
<dic:lastName>
Name
</dic:lastName>
</dic:authorsName>
</dic:author>
<dic:words>
<dic:name>Water</dic:name><br/>
<dic:name>Room</dic:name><br/>
<dic:name>Computer</dic:name><br/>
<dic:name>Book</dic:name><br/>
<dic:name>Garage</dic:name><br/>
<dic:name>Car</dic:name><br/>
<dic:name>Ship</dic:name><br/>
<dic:name>Food</dic:name><br/>
<dic:name>Coffee</dic:name><br/>
<dic:name>Program</dic:name><br/>
</dic:words>
</dic:englishWords>
The path to the list of words is contained in an xml file as follows:
<dic:dictionary xmlns:dic = "dictionary">
<dic:Logo>Logo</dic:Logo>
<dic:Author>User Name</dic:Author>
<dic:EnglishWords>english</dic:EnglishWords>
<dic:SwedishTranslation>swedish</dic:SwedishTranslation>
<dic:SwedishWords>swedish</dic:SwedishWords>
<dic:EnglishTranslation>english</dic:EnglishTranslation>
</dic:dictionary>
my transformation is as follows
<!--Declare a parameter with the nodes to be removed-->
<xsl:param name="removeElementsNamed" select="'|dic:author|dic:title'"/>
<!--create a template and call it remove node-->
<xsl:template match="node()|#*" name="removeNode">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<!--remove the actual nodes-->
<xsl:template match="*">
<xsl:if test="not(contains($removeElementsNamed, concat('|',name(),'|')))">
<xsl:call-template name="removeNode"/>
</xsl:if>
</xsl:template>
I am trying to follow an example which I have found here:
how to exclude elements
...but in my case it does not work.
Any help will be appreciated...
Bluetxxth
So it looks like you're trying to exclude elements based on whether |ELEMENTNAME| is present in $removeElementsNamed, but dic:author is the only item in that list that has pipes on both sides. It might almost work if you did this:
<xsl:param name="removeElementsNamed" select="'|dic:author|dic:title|'"/>
However this is a bit of a hack.
A better approach would to just do something like this:
<xsl:template match="dic:author | dic:title" />
This should exclude dic:author and dic:title from the output.
Another issue is that this template is misnamed:
<xsl:template match="node()|#*" name="removeNode">
What this template would actually do, if it worked, would be to include nodes that were sent its way, but a template can't both have a match attribute and a name attribute. I would suggest rewriting your XSLT to be like this and starting from here:
<!--Declare a parameter with the nodes to be removed-->
<xsl:template match="dic:author | dic:title" />
<!--create a template and call it remove node-->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
You could match the element you don't want but not output anything.
<xsl:template match="nodeToMatch" />
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.