Using functions in XSLT - 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.

Related

how to generate elements inside source xml namespace (xslt 1.0)

I have a simple xml I'm trying to process, something like
<?xml version="1.0" encoding="utf-8"?>
<root>
<foo>1234</foo>
</root>
with an xslt...I want to inject data into this xml to then process it into an excel...so something horrid like this...in psuedo xslt 1.0..but with the relevant piece
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet" xmlns:html="http://www.w3.org/TR/REC-html40" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:kooks ="http://kookerella.com" exclude-result-prefixes="kooks msxsl">
<xsl:template match="/root">
<xsl:variable name="bar">
<xsl:choose>
<xsl:when test="...">
<xsl:copy-of select="foo"/>
</xsl:when>
<xsl:otherwise>
<foo>5678</foo>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:foreach select="msxml:node-set($bar)/foo">
...do some stuff...
</xsl:foreach>
</xsl:template>
</xsl:stylesheet>
now the issue is that the "foo" element I define inside the xslt is in a different namespace (I think), so it isnt matched in the "foreach" processing section...so how do I force this xslt element to live in the correct namespace?
Your stylesheet declares a default namespace:
xmlns="urn:schemas-microsoft-com:office:spreadsheet"
Not sure why this is needed. While it's there, any element created by the stylesheet will inherit this namespace - specifically foo in:
<xsl:otherwise>
<foo>5678</foo>
</xsl:otherwise>
You could prevent this by placing the created element explicitly in no namespace:
<xsl:otherwise>
<foo xmlns="">5678</foo>
</xsl:otherwise>
but there is probably a better way to accomplish whatever purpose this is supposed to accomplish.
P.S. Your title says "how to generate elements inside source xml namespace" - but the source XML has no namespace. If you want to place the copied element in the default namespace, you must recreate it in the target namespace, not copy it. Copy means copy - including the original namespace.

Replace namespace node string value with xslt---what is wrong with my xsl

I am trying to replace namespace string using xslt.
I have the source namespace string and target namespace string in another xml file in the format of "namespace source="xxx" target="xxx"". All source namespace strings in my to-be-transformed xml should be changed to the corresponding target value. Only elements need to be considered as attributes don't have namespace.
I'm using the JDK default xalan xslt processor, which supports xslt 1.0. I'd like to stick to xslt 1.0 if possible, but I can change processor and use xslt 2.0 if really needed.
For example, to-be-transformed input xml:
<root xmlns="http://ns1" xmlns:ns2="http://ns2-old" xmlns:ns3="http://ns3">
<ElementA xmlns="http://ns4-old">
<ElementB/>
<ns2:elementD/>
</ElementA>
</root>
The output xml should be ("http://ns2-old" is changed to "http://ns2-new", and "http://ns4-old" is changed to "http://ns4-new"):
<root xmlns="http://ns1" xmlns:ns2="http://ns2-new" xmlns:ns3="http://ns3">
<ElementA xmlns="http://ns4-new">
<ElementB/>
<ns2:elementD/>
</ElementA>
</root>
Here is my xsl that is not working:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:variable name="nsmapdoc" select="document('my-map-file')"/>
<xsl:key name="nsmap" match="//namespace/#target" use="#source"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<!-- process each element -->
<xsl:template match="element()">
<xsl:for-each select="namespace::*">
<!-- for each namespace node of the element, save the string value-->
<xsl:variable name="sourceuri"><xsl:value-of select="."/>
</xsl:variable>
<!-- get the target value for this namespace-->
<xsl:variable name="targeturi">
<xsl:for-each select="$nsmapdoc">
<xsl:value-of select="key('nsmap', $sourceuri)"/>
</xsl:for-each>
</xsl:variable>
<!-- if target value exists, replace the current namespace node string value with the target value-->
<xsl:if test="$targeturi">
<xsl:value-of select="$targeturi"/>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
I have a few questions:
For the identity template that do the copy, why doing "match="node()|#*" instead of just "match="node()"? Is attribute also a node?
I am not sure if I did correctly to get the namespace string value (like "http://ns1", "http://ns2-old") for the element.
I think I got the key correctly. However, I am not sure if I used the type correctly---looks like targeturi variable is not a string. If key does not have the entry, what will lookup the entry return? In my case, I should replace the namespace value only for the namespace that has an entry in the map.
How to write a new string value for the namespace node?
I need to process each namespace nodes for the element. Is it the right way to define a variable inside ?
please help to see what is wrong with my xsl. Any suggestion is appreciated.
I think you have two, possibly three, separate questions here.
The first question is: how to move elements from one namespace to another, using a "map" of source-to-target namespaces. Let me answer this question first. Given:
XML
<root xmlns="http://ns1" xmlns:ns2="http://ns2-old" xmlns:ns3="http://ns3">
<ElementA xmlns="http://ns4-old">
<ElementB/>
<ns2:ElementD/>
</ElementA>
</root>
map.xml
<root>
<namespace source="http://ns2-old" target="http://ns2-new"/>
<namespace source="http://ns4-old" target="http://ns4-new"/>
</root>
The following stylesheet:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:param name="nsmapdoc" select="document('map.xml')"/>
<xsl:template match="*">
<xsl:variable name="old-ns" select="namespace-uri()"/>
<xsl:variable name="map-entry" select="$nsmapdoc/root/namespace[#source=$old-ns]"/>
<xsl:variable name="new-ns">
<xsl:choose>
<xsl:when test="$map-entry">
<xsl:value-of select="$map-entry/#target"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$old-ns"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:element name="{local-name()}" namespace="{$new-ns}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
will return:
<?xml version="1.0" encoding="utf-8"?>
<root xmlns="http://ns1">
<ElementA xmlns="http://ns4-new">
<ElementB/>
<ElementD xmlns="http://ns2-new"/>
</ElementA>
</root>
Note:
ElementA and its child ElementB have been moved from namespace URI "http://ns4-old" to URI "http://ns4-new";
ElementD has been moved from namespace URI "http://ns2-old" to URI "http://ns2-new";
The prefix of ElementD has been stripped off; this is a meaningless, cosmetic change, and it should not present any problems for the receiving application;
The root element has remained in its original namespace;
Unused namespace declarations have been discarded.
Note also that we are not using a key to lookup the new namespace: using a key across documents is quite awkward in XSLT 1.0 and I have chosen to do without.
The second question is how to copy the unused namespace declarations. There are several possible answers to choose from:
It's not necessary to copy them, since that are not used for anything;
It's not possible to copy them;
It is possible to copy them by copying some element from the source document; however copying an element also copies its namespace - so this can be done only if there is a known element that is supposed to stay in its original namespace. For example, if you know beforehand that the root element is not supposed to be moved to another namespace, you can add another template to the stylesheet:
to get this result:
<?xml version="1.0" encoding="utf-8"?>
<root xmlns="http://ns1" xmlns:ns2="http://ns2-old" xmlns:ns3="http://ns3">
<ElementA xmlns="http://ns4-new">
<ElementB/>
<ElementD xmlns="http://ns2-new"/>
</ElementA>
</root>

XSL Create Element Which Has Colon in Name

This works fine
<xsl:element name="title">
<xsl:value-of select="#title"/>
</xsl:element>
However this doesn't
<xsl:element name="image:title">
<xsl:value-of select="#title"/>
</xsl:element>
Please can someone advise? When I say it doesn't work, the page breaks. Unfortunately due to the nature of the system, I can't see an error
The top of the page reads as follows
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:udf="http://www.virtualfestivals.com/udf">
<xsl:output method="html" omit-xml-declaration="no" />
The error you are getting is probably along the lines of Undeclared namespace prefix {image}. The "image" part of you element name is actually a "namespace prefix", just like how all the xslt elements are prefixed with "xsl:".
To resolve this, you need to add a declaration for the "image" prefix somewhere in your XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:image="my:image"
xmlns:udf="http://www.virtualfestivals.com/udf">
Of course, what the URI is for your "image" depends on what you are need to add the prefix for in the first place. (EDIT: As per your comment, you will need to set it to http://www.google.com/schemas/sitemap-image/1.1)
Note that you don't actually need to use xsl:element here. If the element name is static, just write out the element like so...
<image:title>
<xsl:value-of select="#title"/>
</image:title>
Read up on namespaces at http://www.xml.com/pub/a/2001/04/04/trxml/ for example.

Type of Amazon xml response breaks php xsl

Since Amazon shut off it's xslt support, I wanted to move it to my own server using php5's xsl. My output needs to be in a text format for my JS to process it for a web page. My problem is Amazon's xml response (very abbreviated) looks like this
<?xml version="1.0" ?>
<ItemLookupResponse xmlns="http://webservices.amazon.com/AWSECommerceService/2011-08-01">
/............./
</ItemLookupResponse>
My problem is that my xsl stylesheet works fine as long as I remove the xmlns="http://...". What is needed in a xsl style to have it bypass or just ignore that ?
All the nodes I need are well inside that outer one.
Here is the xslt:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="CallBack" select="'amzJSONCallback'"/>
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:value-of select="$CallBack"/>
<xsl:text>( { "Item" : </xsl:text><xsl:apply-templates/><xsl:text> } ) </xsl:text>
</xsl:template>
<xsl:template match="OperationRequest"></xsl:template>
<xsl:template match="Request"></xsl:template>
<xsl:template match="Items">
<xsl:apply-templates select="Item"/>
</xsl:template>
<xsl:template match="Item">
<xsl:text> {</xsl:text>
<xsl:text>"title":"</xsl:text><xsl:apply-templates select="ItemAttributes/Title"/><xsl:text>",</xsl:text>
<xsl:text>"author":"</xsl:text><xsl:apply-templates select="ItemAttributes/Author"/><xsl:text>",</xsl:text>
<xsl:text>"pubbdate":"</xsl:text><xsl:apply-templates select="ItemAttributes/PublicationDate"/><xsl:text>"</xsl:text>
<xsl:text>} </xsl:text>
</xsl:template>
</xsl:stylesheet>
You should probably learn how XML namespaces work. In a nutshell, you have to define a namespace prefix in your XSL file like this:
<xsl:stylesheet ... xmlns:awse="http://webservices.amazon.com/AWSECommerceService/2011-08-01">
Then, you have to use qualified names to match and select elements under that namespace:
<xsl:template match="awse:ItemLookupResponse">
(With XSLT 2.0, you can define a default namespace. But since you're using PHP, you're probably limited to XSLT 1.0.)
It looks like nwellnhof is correct. I was using the wrong namespace in my testing. All I did was add:
<xsl:stylesheet ... xmlns:aws="http://webservices.amazon.com/AWSECommerceService/2011-08-01">
Then the elements look like
<xsl:template match="aws:ItemLookupResponse">
Now the conversion works perfectly. I don't know why it didn't work the first time I tried it.

How to use a function from one xsl in another

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.