I would like to call a template based on an inbound parameter to an xsl stylesheet.
Using the parameter in the name attribute fails because $ is illegal in the context. Does this mean I have to use a xsl:choose to accomplish this?
If you want to call templates selected dynamically then you can usually do it using xsl:apply-templates rather than xsl:call-template. One very general way of doing this is to change each
<xsl:template name="n">
to
<xsl:template name="n" match="xsl:template[#name='n']">
and then change your invalid
<xsl:call-template name="$x"/>
to a legitimate
<xsl:apply-templates select="document('')/*/xsl:template[#name=$x]">
And pass the context item as a parameter if necessary.
However, if we knew more about the problem you are trying to solve, we might be able to suggest a better way of solving it.
Unless you use an XSLT processor like the commercial version of Saxon 9 where you have an extension instruction like http://www.saxonica.com/documentation/extensions/instructions/call-template.xml you will need to use xsl:choose.
Related
I am trying to pass in several parameters to a scheatron xlst transform, I can declare local variables and read from a file but that still means that I am either hard-coding or using a relative path to the execution location.
Does anyone know of a way to pass in a parameter, something like this is what I really need:
<xsl:param name="doctype"/>
<xsl:param name="id"/>
Any suggestion is welcome, or do I give up on schematron and go back to standard xslt?
Peter
I have a question regarding <xsl:apply-templates>.
Lets assume I have an XML like this (see http://www.tei-c.org/release/doc/tei-p5-doc/en/html/ref-subst.html):
<transcription>
<subst>
<del>wrong</del>
<add>right</add>
</subst>
</transcription>
Now I want to process this recording of a transcription in different ways using XSLT.
If I just want to present the correction to the user, I could use an XSLT template like this:
<xsl:template match="subst"><xsl:apply-templates select="./add"/></xsl:template>
<xsl:template match="subst/add"><xsl:apply-templates/></xsl:template>
However, I could also write:
<xsl:template match="subst"><xsl:apply-templates/></xsl:template>
<xsl:template match="subst/add"><xsl:apply-templates/></xsl:template>
<!-- del: ignore contents -->
<xsl:template match="subst/del"></xsl:template>
In the first case, I explicitly only address add inside <subst>, ignoring <del>.
In the second case, I ignore <del> by providing a template that does not do anything with the element, resulting in the same effect.
If I am not mistaken, the two ways are equivalent. Which one is preferable?
IMHO, not processing nodes at all is preferable to processing them with an empty template. But sometimes the alternative is more convenient, e.g. for reasons of code readability.
I am an open-source xslt package for date format conversion.
Link # http://xsltsl.sourceforge.net/
And this is how I call in order to do my date format conversion and obtain date in the following format - April 4, 2011.
<xsl:variable name="date.format">%B %d, %Y</xsl:variable>
<xsl:variable name="someDate">
<xsl:call-template name="dt:format-date-time">
<xsl:with-param name="xsd-date-time" select="//someDate"/>
<xsl:with-param name="format" select="$date.format"/>
</xsl:call-template>
</xsl:variable>
I retrieve the result by call - <xsl:value-of select="$checkindate"/>
I would like to update my above code to pass in an additional parameter which is language and be able to get translated values with no luck.
Any help!
I would like to update my above code to pass in an additional
parameter which is language and be able to get translated values with
no luck.
The dt:format-date-time template isn't intended to accept any other parameters than it does at present.
There is more than one way to achieve the task specified above:
Modify the code of the dt:format-date-time template by adding an `xsl:param name="lang" and the corresponding code to process the new parameter.
Add a new template (say dt:format-date-time-NL). In this template have an xsl:param name="lang" and, if appropriate, call the existingdt:format-date-time` template.
I recommend the second approach as a positive example of reusing code and achieving flexibility. XSLT provides clean support for this writing style with the standard xsl:import and xsl:include directives.
Note also, that directly editing someone else's code will not always be possible, desirable or ethical. Even if alowed this can lead to multitude of incompatible "versions" (more exactly "mutations") of the original code with all bad consequences
This question actually asks something quite different. See the comments to #Tomalak's answer to understand what the OP really wanted. :(
Is there a way to store a variable/param during a for-each loop in a sort of array, and use it in another template, namely <xsl:template match="Foundation.Core.Classifier.feature">.
All the classname values that appear during the for-each should be stored. How would you implement that in XSLT? Here's my current code.
<xsl:for-each select="Foundation.Core.Class">
<xsl:for-each select="Foundation.Core.ModelElement.name">
<xsl:param name="classname">
<xsl:value-of select="Foundation.Core.ModelElement.name"/>
</xsl:param>
</xsl:for-each>
<xsl:apply-templates select="Foundation.Core.Classifier.feature" />
</xsl:for-each>
Here's the template in which the classname parameters should be used.
<xsl:template match="Foundation.Core.Classifier.feature">
<xsl:for-each select="Foundation.Core.Attribute">
<owl:DatatypeProperty rdf:ID="{Foundation.Core.ModelElement.name}">
<rdfs:domain rdf:resource="$classname" />
</owl:DatatypeProperty>
</xsl:for-each>
</xsl:template>
The input file can be found at http://krisvandenbergh.be/uml_pricing.xml
No, it is not possible to store a variable in a for-each loop and use it later.
This is because variables are write-once in XSLT (once set they are immutable) and they are strictly scoped within their parent element. Once processing leaves the for-each loop, the variable is gone.
XSLT does not work as an imperative programming language, but that's what you seem to be trying here. You don't need <xsl:for-each> in 98% of all cases and should not use it because it clogs your view of how XSLT works. To improve your XSLT code, get rid of all <xsl:for-each> loops you have (all of them, I mean it) and use templates instead:
<xsl:template match="Foundation.Core.Class">
<xsl:apply-templates select="
Foundation.Core.Classifier.feature/Foundation.Core.Attribute
" />
</xsl:template>
<xsl:template match="Foundation.Core.Attribute">
<owl:DatatypeProperty rdf:ID="{Foundation.Core.ModelElement.name}">
<rdfs:domain rdf:resource="{
ancestor::Foundation.Core.Class[1]/Foundation.Core.ModelElement.name[1]
}" />
</owl:DatatypeProperty>
</xsl:template>
(I'm not sure if the above is what you actually want, your question is rather ambiguous.)
Note the use of the XPath ancestor axis to refer to an element higher in the hierarchy (you seem to want the <Foundation.Core.ModelElement.name> of the parent class).
PS: Your XML is incredibly bloated and strongly redundant due to structured element names. Structure should come from... well... structure, not from elements like <Foundation.Core.Classifier.feature>. I'm not sure if you can do anything about it, though.
Addition:
To solve your xmi.id / xmi.idref problem, the best way is to use an XSL key:
<!-- this indexes all elements by their #xmi.id attribute -->
<xsl:key name="kElementByIdref" match="*[#xmi.id]" use="#xmi.id" />
<!-- now you can do this -->
<xsl:template match="Foundation.Core.DataType">
<dataTypeName>
<!-- pull out the corresponding element from the key, output its value -->
<xsl:value-of select="key('kElementByIdref', #xmi.idref)" />
</dataTypeName>
</xsl:template>
To better understand how keys work internally, you can read this answer I gave earlier. Don't bother too much with the question, just read the lower part of my answer, I explained keys in terms of JavaScript.
Ok, I now understand why for-each is not always needed. Consider the code below.
<Foundation.Core.DataType xmi.id="UID71848B1D-2741-447E-BD3F-BD606B7FD29E">
<Foundation.Core.ModelElement.name>int</Foundation.Core.ModelElement.name>
</Foundation.Core.DataType>
It has an id UID71848B1D-2741-447E-BD3F-BD606B7FD29E
Way elsewhere I have the following.
<Foundation.Core.StructuralFeature.type>
<Foundation.Core.DataType xmi.idref="UID71848B1D-2741-447E-BD3F-BD606B7FD29E"/>
</Foundation.Core.StructuralFeature.type>
As you can see, both codes have the same ID. Now I want to output "int", everytime this ID appears somewhere in the document. So basically idref="UID71848B1D-2741-447E-BD3F-BD606B7FD29" should be replaced by int, which can be easily derived from the Foundation.Core.ModelElement.name element.
Above is the main reason why I would like to store it in a variable. I don't get it how this can be dealt with using XSLT. If someone could elaborate on this, I hope there exists some kind of pattern to solve such a problem, since I need it quite often. What would be a good approach?
I understand this is maybe a bit off-topic, but I am willing to ask it in this thread anyway since this problem is very close to it.
If one is using the document function and opening up files that might not exist e.g.
<xsl:variable name="input" select="document($A)/document/input"/>
what is the graceful way of handling the error? I would like a default value for the variable if the file can't be opened.
There isn't a general way to handle gracefully an error in the document() function.
According to the XSLT 1.0 spec:
" If there is an error retrieving the resource, then the XSLT processor may signal an error; if it does not signal an error, it must recover by returning an empty node-set. "
This means that we are at the mercy of the implementor whether an empty node-set is produced by the function (good, we can test for empty (non-existent) node-set) or "signals an error", which typically may end the transformation.
In case we have checked that a particular implementation of a specific XSLT processor only produces an empty node-set and does not end the transformation, we may decide to test for this condition and "gracefully" recover. However, our application becomes non-portable, it depends on this specific XSLT processor and there is absolutely no guarantee that in the next version this behaviour will not change to the worse one. Risky, isn't it?
Therefore, it is best that whoever launches the transformation (such as from within a C# program), should check for the existence of the file and pass an appropriate parameter to the transformation, reflecting this existence.
What about using doc-available() function available in XPath 2.0 ? :
http://www.w3.org/TR/xpath-functions/#func-doc-available
I believe you can write your <xsl:variable> like so:
<xsl:variable name="input">
<xsl:choose>
<xsl:when test="document($A)/document/testElementCondition = 'foo'">
<xsl:value-of select="document($A)/document/input" />
</xsl:when>
<xsl:otherwise>
<!-- add some default source document and logic that will then direct to an error message. -->
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
It is too bad that you often have to result to hacks to get things done in XSL.
The graceful way would be to check file existence before you feed the parameter to your stylesheet.