Please help me out guys. I'm just trying to declare a simple result tree fragment and iterate over it.
...
<xsl:variable name="rtf">
<item-list>
<item id="1">one</item>
<item id="2">two</item>
<item id="3">three</item>
<item id="4">four</item>
</item-list>
</xsl:variable>
<xsl:for-each select="msxsl:node-set($rtf)/item-list/item">
<xsl:value-of select="#id"/>
</xsl:for-each>
...
Am I completely mistaken about how this works?
Edit:
I'm using .NET XslCompiledTransform and have the correct msxsl namespace declarations - xmlns:msxsl="urn:schemas-microsoft-com:xslt"
The transformating executes fine - the problem is that nothing is output
My suspicion is that you have a default namespace declared in your stylesheet. That would effectively place the <item-list> and <item> elements into a namespace. To select namespace-qualified elements using XPath 1.0, you must always use a prefix in the expression.
So if you have something like this at the top of your stylesheet:
<xsl:stylesheet xmlns="http://example.com"...>
Then you'll need to also add this:
<xsl:stylesheet xmlns="http://example.com" xmlns:x="http://example.com"...>
And then use the "x" prefix in your XPath expression:
<xsl:for-each select="msxsl:node-set($rtf)/x:item-list/x:item">
<xsl:value-of select="#id"/>
</xsl:for-each>
Let me know if that did the trick. I'm only speculating here.
Unlike MSXSL, XslCompiledTransform provides node-set() function where it is supposed to be - in EXSLT Common namespace:
<xsl:stylesheet xmlns:exslt="http://exslt.org/common">
...
<xsl:for-each select="exslt:node-set($rtf)/item-list/item">
...
</xsl:stylesheet>
This looks OK to me.
Have you correctly declared the msxsl namespace for the extension functions though? Something like this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
I am assuming you are using the Microsoft XSLT processor here
Related
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.
I have below XML and would like to iterate through the element as such the i could display it in some format like:
PIN 1<br/>
XYZ<br/>
HELLO<br/>
PIN 2<br/>
ABC<br/>
HI<br/>
XML:
<RootResponse xmlns:ip="urn:domain:tx:inPayment" xmlns:ipn="urn:domain:tx:Pin">
<OutBoundMessage>
<ip:InfoMessage>
<ipn:Alert>PIN 1</ipn:Alert>
<ipn:Code>
<ip:CodeLabel>XYZ</ip:CodeLabel>
<ip:CodeMessage>HELLO</ip:CodeMessage>
</ipn:Code>
</ip:InfoMessage>
<ip:InfoMessage>
<ipn:Code>
<ipn:Alert>PIN 2</ipn:Alert>
<ip:CodeLabel>ABC</ip:CodeLabel>
<ip:CodeMessage>HI</ip:CodeMessage>
</ipn:Code>
</ip:InfoMessage>
</OutBoundMessage>
</RootResponse>
I Can't seem to find a solution. Any suggestion?
I would recommend following the W3C schools XSLT tutorial, this should give you all you need to solve this relatively simple XSLT problem.
You are right that you will have to pay attention to namespaces, although again this is quite straightforward. Simply ensure that your XSLT defines the namespaces required, and that you prefix element names in your XPath statements accordingly. See the following:
XML Namespaces and How They Affect XPath and XSLT
You should declare the namespaces in your XSLT and then use the declared prefix in your expressions.
Below is an example of how to do that, using templates(i.e. "push style") rather than xsl:for-each (e.g. "pull style").
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ip="urn:domain:tx:inPayment"
xmlns:ipn="urn:domain:tx:Pin"
exclude-result-prefixes="ip ipn">
<xsl:output indent="yes" />
<xsl:template match="ipn:Alert">
<xsl:text>
</xsl:text>
<xsl:apply-templates />
<br/>
</xsl:template>
<xsl:template match="ip:*[starts-with(local-name(),'Code')]">
<xsl:text>
</xsl:text>
<xsl:apply-templates/>
<br/>
</xsl:template>
</xsl:stylesheet>
I have declared a variable in my XSLT as given below :
<xsl:variable name="inline-array">
<Item>A</Item>
<Item>B</Item>
<Item>C</Item>
</xsl:variable>
I am accessing this variable as given below :
<xsl:param name="array"
select="document('')/*/xsl:variable[#name='inline-array']/*" />
<xsl:value-of select="$array[1]" />
This is working fine as long as my inline array has static contents. But my requirement is to dynamically assign values in the XSLT to the tag "Item" ie. Something like :
<xsl:variable name="inline-array">
<Item>$item1</Item>
<Item>$item2</Item>
<Item>$item3</Item>
</xsl:variable>
But, I tried all possible options without any luck. Any suggestions will be greatly appreciated. Any other options to fulfill my requirement is also welcome. Thanks.
One way to achieve this is to make use of an extension function, namely the node-set function, which returns a set of nodes from a result tree fragment.
First you would need to define the namespace for the extension functions like so
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt">
In this case, I am using the Microsoft extension functions, but others are available depending on which platform you are using. (http://exslt.org/common is another common one for non-Microsoft platforms).
Next, you define your "array" parameter (or variable, you wanted), like so.
<xsl:param name="array" select="msxsl:node-set($inline-array)"/>
Finally, you can then access this array like so
<xsl:value-of select="$array/Item[1]"/>
Putting this altogether in a simple example gives you this
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:output method="text" />
<xsl:variable name="inline-array">
<Item>
<xsl:value-of select="$Item1"/>
</Item>
<Item>
<xsl:value-of select="$Item2"/>
</Item>
<Item>
<xsl:value-of select="$Item3"/>
</Item>
</xsl:variable>
<xsl:param name="Item1">1</xsl:param>
<xsl:param name="Item2">2</xsl:param>
<xsl:param name="Item3">3</xsl:param>
<xsl:param name="array" select="msxsl:node-set($inline-array)"/>
<xsl:template match="/">
<xsl:value-of select="$array/Item[1]"/>
</xsl:template>
</xsl:stylesheet>
When run, this simply outputs the following result:
1
Firstly, are you stuck with XSLT 1.0? Workarounds like accessing the stylesheet source code using document('') are very rarely needed if only you can move to XSLT 2.0.
Secondly, I think we need to look at the design of the stylesheet, and we can't really do that without a description of the problem you are trying to solve (as distinct from your attempts at a solution.)
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.
I need to transform an XML file to another XML file, where the source file has a dynamic namespace set to xmlns="whatever". My XSLT runs fine without the namespace being in the file, but I get no output with the namespace. How can I cause the schema of the source file to be applied to the destination file?
All help is appreciated and thanks in advance!
EDIT:
I'm trying to copy the namespace uri over to the resulting file:
<xsl:param name="schema">
<xsl:value-of select="namespace-uri()" />
</xsl:param>
<xsl:element name="root" namespace="$schema">
I have verified that schema is holding the correct value, but the problem is that the program appears to take this too literally:
<root xmlns="$schema">
Is this the right way to go about this?
EDIT x2:
I've implemented Alejandro's suggestion of:
<xsl:element name="root" namespace="{$schema}"/>
And that works for the most part, except for the fact that I have to put the namespace on every element or else I get the following structure in the result:
<root xmlns="NAMESPACE">
<foo xmlns="">
etc.
Is there a way to blanket all of the elements with this namespace, other than putting namespace={$schema} on every single line? Bounty and accept for the best answer!
EDIT x3:
Better example:
If I do:
<xsl:element name="root" namespace="{namespace-uri()}>
<xsl:element name="foo">
<xsl:element name="bar">
<!--etc-->
</xsl:element>
</xsl:element>
</xsl:element>
I get:
<root xmlns="NAMESPACE">
<foo xmlns="">
<bar>
<!--etc-->
</bar>
</foo>
<root>
I would like to have them all under namespace NAMESPACE, so I did:
<xsl:element name="root" namespace="{namespace-uri()}>
<xsl:element name="foo" namespace="{namespace-uri()}>
<xsl:element name="bar" namespace="{namespace-uri()}>
<!--etc-->
</xsl:element>
</xsl:element>
</xsl:element>
However this is ugly and tedious to type. Is there an easier way to blanket the namespace over all elements? (hopefully this clarifies what I need)
Suppose you have this XML input:
<root xmlns="survivors">
<louis/>
<francis/>
</root>
Meaning that every element is under default namespace wich its URI is "survivors".
As Welbog wrote you can select francis element with:
/*/*[local-name()='francis']
or
/*[local-name()='root']/*[local-name()='francis']
But, that also select francis element from these XML inputs:
<root xmlns="survivors" xmlns:n="no-survivors">
<louis/>
<n:francis/>
</root>
or
<root xmlns="survivors">
<louis/>
<francis xmlns="no-survivors"/>
</root>
You could also strengthen the predicate with some namespace URI. But, wich one? An option could be the default namespace for root element like:
/*/*[local-name()='francis'][namespace-uri()=namespace-uri(/*)]
Surely this make XPath expression very verbose.
In XSLT 2.0 you could use xsl:xpath-default-namespace attribute like:
<xsl:value-of select="/root/francis" xpath-default-namespace="survivors"/>
But that's not good for your case because you don't know the URI in advance.
EDIT: xsl:element 's attributes are AVT (Attribute Value Template) so you need this:
<xsl:element name="root" namespace="{$schema}"/>
Also, I recomend you to declare the param as a string data type (not RTF like now), something like:
<xsl:param name="schema" select="namespace-uri()"/>
EDIT 2: Maybe I was not clear. You don't need xsl:element/#namespace in every case. Following your statement that every element is in only one default namespace, this stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[local-name()='bar']">
<xsl:element name="newbar" namespace="{namespace-uri()}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
With this input:
<root xmlns="whatever">
<foo/>
<bar/>
</root>
Output:
<root xmlns="whatever">
<foo></foo>
<newbar></newbar>
</root>
Edit 2: I was showing you that when you are copying an element you are also copying the namespace needed for expand the QName. So, if want to transform this:
<root xmlns="whatever">
<foo/>
<bar/>
</root>
Into this:
<root xmlns="whatever">
<foo>
<bar/>
</foo>
</root>
You can use this stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="*">
<xsl:copy>
<xsl:apply-templates select="*[1]|following-sibling::*[1]"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Since the namespace is part of the full name of any elements your XPath is referencing, and since you don't know the namespace of the source file in advance, you'll have to use the local names of the elements you're accessing instead of their full names.
Let's say you have a source file like this:
<root xmlns="survivors">
<louis/>
<francis/>
</root>
One way to access these elements using XSLT is to specify a namespace that matches the source file's default namespace:
<xsl:stylesheet
version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:surv="survivors"
>
<xsl:template match="surv:louis">
<!-- ETC -->
That way works when you know the namespace. When you don't know the namespace, you can ignore it using the XPath function local-name() like this:
<xsl:stylesheet
version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:template match="*[local-name() = 'louis']">
<!-- ETC -->
Using local-name() means you can ignore the namespace in the source document. You'll have to be careful if there are multiple elements with the same local name in different namespace, though. It's not exactly a robust solution, but if you can't trust your namespace then you don't have that many options anyway.
I'd imagine that having a variable namespace is a bigger problem in and of itself. If that's under your control, you should correct it. If it's not under your control, you should push to have it corrected.
XSLT is rarely executed in a vaccum. It's almost always a part of some other application that loads the XSLT, loads the source document, and then produces the output.
Assuming the above is your scenario, I wouldn't solve this problem directly with XSLT. Instead, I'd use standard XML technologies to examine the source XML document and discover the default namespace. Then, I'd load the XSLT document and use string substitution or some other technique to inject the resultant namespace at the appropriate point in the transform. Then I'd go ahead and run the transform normally.
This will make your XSLT much more natural to write and maintain. I'm a pro with XSLT and use it constantly, and this is how I would solve it. I couldn't imagine the ugliness of a stylesheet that had to use local-name() comparisons constantly. What a pain. (Of course, in much more complex scenarios there may be no choice. Fortunately yours isn't one of them.)
If you don't have this option, I sympathize.