How to use a function from one xsl in another - xslt

I have two xsl files: "one.xsl" and "two.xsl"
one.xsl:
<xsl:function name="x:trans" as="xs:string">
<xsl:param name="str"></xsl:param>
<xsl:variable name="res1" select="x:translate_string($str)"/>
<xsl:sequence select="$res1"/>
</xsl:function>
</xsl:stylesheet>
I want to use function "x:trans" in "one.xsl"
How do i reference the function to another file?
The problem is that when i try to call for this function this way:
< xsl:value-of select="x:trans('Hello World')"/>
I get the following error message from browser:
Reference to undeclared namespace prefix: 'x'

Apart from the correct replies that you need to <xsl:include> or <xsl:import> (I'd recommend the latter as the former can often result in duplication errors), your other problem is the following:
A function name must belong to a namespace.
The namespace must be declared (defined and bound to a prefix) in the same file in which the function is defined.
Any call to the function has to prefix the name of the function and that prefix must be bound to the same namespace to which the function name belongs
Here is a simple example:
I. File deleteA.xsl defines the function my:double
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:my="my:my"
>
<xsl:function name="my:double" as="xs:double">
<xsl:param name="pArg" as="xs:double"/>
<xsl:sequence select="2*$pArg"/>
</xsl:function>
</xsl:stylesheet>
II. File deleteB.xsl imports file deleteA.xsl and uses the function my:double :
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:my="my:my">
<xsl:import href="deleteA.xsl"/>
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:sequence select="my:double(.)"/>
</xsl:template>
</xsl:stylesheet>
III. The transformation contained in deleteB.xsl is applied on the following XML document:
<t>1</t>
and the correct result is produced:
2
Additional comment: At present no browser supports XSLT 2.0 transformations -- xsl:function is only available in XSLT 2.0 +.

You want to either do <xsl:include /> or <xsl:import />. <xsl:include /> is simpler (it just drags everything in) while <xsl:import /> is more flexible (if there are templates colliding between the two, the over-ride of the called by the calling is better defined and generally sensible).
Edit for added info:
You need to make sure you call the templates in the imported stylesheet using the appopriate namespace. The easiest way is to make sure you have matching xmlns:foo declarations in the stylesheets, though you could call foo:template in one stylesheet as bar:template in the other if it had xmlns:bar instead.

In two.xsl:
<xsl:include href="one.xsl" />
Also see the description of include in the XSLT 2.0 spec.

Related

Best way to include xslt inside another xslt when just a variable needs redefining

I've got 4 almost identical stylesheets (in XSLT 1.0), that are copy pasted, so 2 of them would look like this.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:output method="xml" indent="yes"/>
<xsl:variable name="foo" select="input[#name = 'foo1']"/>
<xsl:template match="/">
<foo>
<xsl:value-of select="$foo"/>
</foo>
</xsl:template>
</xsl:stylesheet>
and another
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:output method="xml" indent="yes"/>
<xsl:variable name="foo" select="input[#name = 'foo2']"/>
<xsl:template match="/">
<foo>
<xsl:value-of select="$foo"/>
</foo>
</xsl:template>
</xsl:stylesheet>
as you see the only difference is the definition of the variable foo (and in reality the xslt itself is much much more complex).
whats the best way to just define the xslt once, and have 4 wrappers that simply decorate this shared one with a different definition of the variable.
There seems to be at least 3 options, include, import and then XML option XInclude etc?
Is there an idiomatic way to do this? (I assume this is a pretty common requirement).
The basic approach is to put the common code in a shared module that's imported by all the 4 wrappers.
In the shared module you can either declare the variable with a dummy value, or not declare it at all.
In the wrapper module, you just need two declarations:
<xsl:import href="common.xsl"/>
<xsl:variable name="foo" select="input[#name = 'foo2']"/>
With XSLT 3.0 you can get a lot more elaborate using packages, in which declarations can be annotated as private, abstract, final etc.

exsl:document - trying to generate output file

I have this code so far: - now updated with different code
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl"
exclude-result-prefixes="exsl"
version="1.0">
<xsl:output method="xml"/>
<xsl:variable name="emailPID" select="attr[#tag='00100020']"/>
<xsl:variable name="emailPName" select="attr[#tag='00100010']"/>
<!-- overwritten by application with actual values -->
<xsl:param name="calling" select="'SAMPLE_MOD'"/>
<xsl:param name="called" select="'SERVER1'"/>
<xsl:param name="date" select="'20051206'"/>
<xsl:param name="time" select="'115600.000'"/>
<xsl:template match="/dataset">
<exsl:document href="file:///c|/apps/foo.txt">
<xsl:copy-of select="$emailPID"/>
<xsl:copy-of select="$emailPName"/>
</exsl:document>
</xsl:template>
</xsl:stylesheet>
The transformer doesn't throw any errors that I see, but I cannot see the "c:\apps\foo.txt" file I am expecting either. Is there some formatting wrong here or am I leaving something out?
thank you for looking
The processor should throw an error when it sees
extension-element-prefixes="exslt"
because the prefix "exslt" has not been declared. Perhaps you meant "exsl". At present, "exsl" is not declared as an extension namespace, therefore "exsl:document" is a simple literal result element rather than an instruction.
The href attribute of an exsl:document needs to be a valid URI. The XSLT engine is probably confusing the part before the colon (i.e. c) as a URI scheme, not part of the path.
If you are using an absolute address for the filesystem, include the file: URI scheme:
<exsl:document href="file:///c:\apps\foo.txt">
The drive colon ad slashes may cause problems on non-windows platforms so you can instead try:
<exsl:document href="file:///c|/apps/foo.txt">

Use XSLT to copy XML without the xml declaration

I have the following xml and want the output to not contain the xml declaration
i.e.
FROM
<?xml version="1.0" encoding="UTF-8"?>
<tns:MFTRNS xmlns:tns="MFTRNS" recordState="New" msgVersion="13.0">
<OSCONO>100</OSCONO>
<OSINOU>1</OSINOU>
<OSDLIX>155379</OSDLIX>
<OSPANR>AAG44780</OSPANR>
<OSWHLO>AAG</OSWHLO>
</tns:MFTRNS>
TO
<tns:MFTRNS xmlns:tns="MFTRNS" recordState="New" msgVersion="13.0">
<OSCONO>100</OSCONO>
<OSINOU>1</OSINOU>
<OSDLIX>155379</OSDLIX>
<OSPANR>AAG44780</OSPANR>
<OSWHLO>AAG</OSWHLO>
</tns:MFTRNS>
Can you get an xslt to do this and if so how?
The reason for doing this is that I want to wrap the xml in an envelope which cannot be done if the declaration is a part of the XML as it does not create a valid xml file
Thanks
If you only want to remove the declaration then a stylesheet as simple as this will do it:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" omit-xml-declaration="yes" />
<xsl:template match="/">
<xsl:copy-of select="node()" />
</xsl:template>
</xsl:stylesheet>
But if your ultimate aim is to "wrap the xml in an envelope" then you might be better doing that directly in your XSLT, for example:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" />
<xsl:template match="/">
<soap:Envelope xmlns:soap="http://www.w3.org/2001/12/soap-envelope">
<xsl:copy-of select="node()" />
</soap:Envelope>
</xsl:template>
</xsl:stylesheet>
which will be safer than trying to combine the two files using non-XML-aware textual operations. For example, if your envelope declares a default namespace xmlns="http://example.com" then simply inserting the text of another XML document inside the envelope would change the semantics as it would move the non-prefixed elements like OSCONO into the envelope's default namespace when they should really be in no namespace. XSLT will spot this case and add the necessary xmlns="" overrides.
It's simple: you have to set the omit-xml-declaration attribute of your xsl:output element to yes.

Using functions in XSLT

I'm learning XSLT. These questions may be obvious, but I'm really stuck now.
Oxygen returns the following two kind of errors:
Namespace is not declared for 'ownFunction()'. ("undeclared namespace prefix {xs}")
unknown system function index-of-string() The XSLT function index-of-string I got from this website doesn't seems to be recognized
This is a simplified version of the XSL file:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0" xmlns:foo="http://www.wathever.com">
<xsl:output method="xml" />
<xsl:function name="foo:ownFunction" as="xs:string">
<xsl:param name="string" as="xs:string"/>
<xsl:choose>
<xsl:when test='contains($string,"src=")'>
<xsl:variable name="position"><xsl:value-of select="index-of-string($string,'src=')"/>+<xsl:number value="10"/></xsl:variable>
<xsl:variable name="partString"><xsl:value-of select="substring($string,$position)"/></xsl:variable>
<xsl:variable name="length"><xsl:value-of select="index-of-string($partString,'quot;')"/> - <xsl:number value="2"/></xsl:variable>
<xsl:value-of select="substring($partString,1,$length)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="hotpot-jmatch-file/data/title"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
<xsl:template match="/">
<data>
<title>
<xsl:variable name="string"><xsl:value-of select="hotpot-jmatch-file/data/title"/></xsl:variable>
<xsl:value-of select="foo:ownFunction($string)"/>
</title>
</data>
</xsl:template>
</xsl:stylesheet>
Oxygen returns the following two kind
of errors:
1) Namespace is not declared for
'ownFunction()'. ("undeclared
namespace prefix {xs}")
This is actually an XML issue. Any XSLT stylesheet myst be a well-formed XML document. Among other requirements for well-formedness, any namespace prefix used must be bound to a namespace URI in a namespace declaration.
To correct this bind the "xs" prefix to "http://www.w3.org/2001/XMLSchema" -- this means to add xmlns:xs="http://www.w3.org/2001/XMLSchema" to an element (usually the top element is a good choice for this namespace.
You have the same problem with "foo:ownFunction". You must have the prefix "foo" bound/defined and visible, before using it. Just add xmlns:foo="my:foo" to the top element of your stylesheet.
2) "unknown system function
index-of-string()". The XSLT function
"index-of-string" I got from this
website doesn't seems to be
recognized:
http://www.xsltfunctions.com/xsl/functx_index-of-string.html
You have forgotten to either copy and paste the function from Priscilla Walmsley's site or to save it in a separate file (recommended) and then use <xsl:import> or <xsl:include> to import/include this stylesheet file to your transformation.
Finally, such issues show that you need a more systematic introduction of XSLT. Get a good book and read it well. You won't be sorry. This answer may be useful in listing what I consider good XSLT and XPath learning resources.
Use
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0" xmlns:foo="http://www.wathever.com"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs functx""
xmlns:functx="http://www.functx.com">
<xsl:import href="location-of-functx-library.xsl"/>
...
<xsl:value-of select="functx:index-of-string($partString,'quot;')"/>
That samples defines the schema namespace and binds it to the prefix xs, defines the namespace of the function library you linked to. You will also need to download the function library implementation and import it as shown.

can you branch in xslt depending on output format?

I'm creating an xlst script and was wondering if it is possible to branch some code depending on the output format?
On top of my xlst file I have this:
<xsl:output
version="4.0"
method="html"
indent="no"
encoding="UTF-8"
use-character-maps="spaces"/>
So I suppose there is something out there to inquire some sort of global to do this:
<xsl:if test='global_output is html'>
do this
</xsl:if>
Thank you!
If you want to create variants of a stylesheet for use in different situations, don't put if/then/else code inside the template rules to test the condition at run-time. That way you end up with spaghetti. Create two stylesheet modules to-html.xsl and to-xml.xsl, and have both import a module common.xsl that contains the shared code. The common.xsl module can call back to the importing module when it needs to invoke functionality that varies between the two cases. One of the differences between the two cases is of course the xsl:output declaration itself.
In 1.0 one can use:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output version="4.0"
method="html" indent="no" encoding="UTF-8"/>
<xsl:template match="/*">
<xsl:if test="document('')/*/xsl:output/#method = 'html'">
Output method is HTML
</xsl:if>
<xsl:if test="document('')/*/xsl:output/#method = 'xml'">
Output method is XML
</xsl:if>
</xsl:template>
</xsl:stylesheet>
May be there is a classier way to do it in XSLT 2.0.