I would like to dynamically generate xmlns attributes.
I want to generate this in XSL :
<Common:MainPageBase xmlns:Common="clr-namespace:ThisPartIsDynamic;assembly=ThisPartIsDynamic">
</Common:MainPageBase>
How can I do that in XSL?
Thanks,
Alex
Update:
To be more precise, here is what I need to generate. The parts that I want to be able to change with variables are "THISPARTISDYNAMIC":
<Common:MainPageBase
xmlns:Common="clr-namespace:THISPARTISDYNAMIC;assembly=THISPARTISDYNAMIC"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:df="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.DataForm.Toolkit"
xmlns:controlsToolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit"
xmlns:basics="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
xmlns:uc="clr-namespace:THISPARTISDYNAMIC"
mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480"
></Common:MainPageBase>
Any ideas?
You can set the namespace of an element dynamically:
<param name="ns1" >http://localhost/ns1</param>
...
<xsl:element name="test" namespace="{$ns1}" >... </xsl:element>
But that doesn't output a namespace prefix -- it changes the default namespace on that element.
I don't think there is a way to output the prefixes with a dynamic namespace URI.
Something like: <xyz:test xmlns:xyz="{$ns1}">
outputs exactly that literally: <xyz:test xmlns:xyz="{$ns1}">
If that is really the exact output you require, then I think you either have
to modify the serializer, or just produce the output with a placeholder URI and
do a text replacement on the output xml text.
[ XSLT does not process XML syntax. It processes XML trees.
Parsing the input and serializing the output are outside of it's realm. ]
Take a look at the article Namespaces in XSLT, and at the section XSLT 1.0: Creating dynamic namespace nodes in particular.
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes"/>
<xsl:variable name="vDynamicPart1" select="'DynPArt1'"/>
<xsl:variable name="vDynamicPart2" select="'DynPArt2'"/>
<xsl:template match="/">
<xsl:element name="Common:MainPageBase"
namespace="clr-namespace:{$vDynamicPart1};assembly={$vDynamicPart2}"/>
</xsl:template>
</xsl:stylesheet>
when applied on any XML document (not used), produces the desired result.
Related
I have a input xml with a default namespace. eg as below.
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns="aaa">
<subroot>
<country>aaa</country>
<country>bbb</country>
<country>ccc</country>
</subroot>
</root>
While transforming I use xpath-default-namespace="aaa" because otherwise xpaths will not match. Again I have to read a lookup xml using xsl key function. eg as below
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xpath-default-namespace="aaa" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:variable name="LookupDoc" select="document('lookup.xml')" />
<xsl:key name="ObjectType-lookup" match="lookup" use="#att1" />
<xsl:template match="//country">
<countrynew>
<xsl:apply-templates select="$LookupDoc/*">
<xsl:with-param name="curr-code" select="string(.)" />
</xsl:apply-templates>
</countrynew>
</xsl:template>
<xsl:template match='lookups'>
<xsl:param name="curr-code" />
<xsl:value-of select="key( 'ObjectType-lookup' , normalize-space($curr-code))/#att2" />
</xsl:template>
with default namespace in stylesheet element xpath "//country" works fine. The problem arise when I read the lookup xml which doesn't have any namespace. eg:
<?xml version="1.0" encoding="UTF-8"?>
<x:lookups>
<lookup att1="aaa" att2="zzz"/>
<lookup att1="bbb" att2="yyy"/>
<lookup att1="ccc" att2="xxx"/>
</x:lookups>
Is there any way that I can specify in template maching "lookups" to ignore xpath-default-namespace or to match any namespace including no namespce?
Thank you
Is there any way that I can specify in template maching "lookups" to ignore xpath-default-namespace or to match any namespace including no namespce?
You can specify xpath-default-namespace anywhere in the stylesheet: an XPath expression will look up the tree and use the "nearest ancestor" value.
For any element in the stylesheet, this attribute has an effective value, which is the value of the [xsl:]xpath-default-namespace on that element or on the innermost containing element that specifies such an attribute
(From the XSLT 2.0 spec)
So you could say
<xsl:template match='lookups' xpath-default-namespace=''>
to override the default namespace specified on the xsl:stylesheet element. You can even specify it on a literal result element in the stylesheet, as xsl:xpath-default-namespace:
<something xsl:xpath-default-namespace="bbb" attr="{foo}" />
This would create a <something attr="xxx" /> where xxx is the value of the {bbb}foo child element of the current context node.
I did solve problem but sure there will be other ways. What I did was I move template match for lookup and key xsl function to a different document and in xsl stylesheed element I put xpath-default-namespace="". So for those xpath matching xsl use default namespace as none.
Still I'm curious weather there is a way to specify in template itself to use no namespace while matching.
I'm using Java to transform an XML document to text:
Transformer transformer = tFactory.newTransformer(stylesource);
transformer.transform(source, result);
This seems to work except when there are colons in the XML document. I tried this example:
XML file:
<?xml version="1.0" encoding="UTF-8"?>
<test:TEST >
<one.two:three id="my id" name="my name" description="my description" >
</one.two:three>
<one.two:three id="some id" name="some name" description="some description" />
</test:TEST>
XSL file:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xmi="http://www.omg.org/XMI"
xmlns:one.two="http://www.one.two/one.two:three" >
<xsl:output method="text" indent="yes" omit-xml-declaration="yes"/>
<xsl:variable name="myVariable">one.two:three</xsl:variable>
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="*[substring(name(),1,9)='test:TEST']" >
<xsl:for-each select="./$myVariable">
inFirstLoop
</xsl:for-each>
<xsl:for-each select="./one.two:three">
inSecondLoop
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
The result of the transformation I'm getting is a single line:
inFirstLoop
I'm expecting 4 lines of output
inFirstLoop
inFirstLoop
inSecondLoop
inSecondLoop
How do I fix this? Any help is greatly appreciated. Thanks.
There are multiple things wrong here. I'm surprised your transformation managed to run at all, instead of failing on parse errors and other errors.
One big problem is that your input XML uses namespace prefixes (that's what the colons are for) without declaring them. Declarations like
xmlns:one.two="http://www.one.two/one.two:three"
need to be in the source XML, as well as in the XSL. Otherwise your source XML is not well-formed (according to namespace rules).
A second problem is the XPath expression
./$myVariable
which should have thrown an error. I think what you wanted was
*[name() = $myVariable]
The third change I would make is not an error in the XSLT, but just a poor way of doing things... If you want to match <test:TEST>, you should use namespace tools to refer to namespaces. Therefore, instead of
<xsl:template match="*[substring(name(),1,9)='test:TEST']" >
use
<xsl:template match="test:TEST">
Much cleaner. Then you need to put in a namespace declaration on the outermost element of the stylesheet, as you already have to do in the input XML document:
xmlns:test="...test..."
XML namespaces, like driving a car, are a topic better learned from a little training than by trial-and-error. Reading a brief article like this will help you avoid a lot of confusion and pain down the road.
I am trying to transform RSSTV XML using XSLT.
The problem I am having is that I need the XML to look like this:
<?xml version="1.0"?><rss xmlns:tv="http://www.rss-tv.org/rss/tv1.0" version="2.0"><channel>
However, I am unable to create the rss element with this attribute.
I tried using <xsl:attribute> but failed to achieve it.
Just write:
<rss xmlns:tv="http://www.rss-tv.org/rss/tv1.0" version="2.0">
<channel/>
</rss>
xmlns:tv="http://www.rss-tv.org/rss/tv1.0" isn't an attribute -- it is a namespace definition and defines a namespace node belonging to the rss element.
I don't really understand what your problem is :
Here are two ways to do it.
1) Assuming namespace is hardcoded in your xslt.
<xsl:template match="/">
<rss xmlns:tv="http://www.rss-tv.org/rss/tv1.0" version="2.0"></rss>
</xsl:template>
2) Assuming you get the namespace from some other parameter :
<xsl:template match="/">
<xsl:variable name="namespace">http://www.rss-tv.org/rss/tv1.0</xsl:variable>
<rss xmlns:tv="{$namespace}"/>
</xsl:template>
Create an element with your desired result.
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.
I have a question about some sort af random function in XSLT.
I have an XML-file that very simplified look similar to this:
<node id="1198">
<node id="1201">
<data alias="name">Flemming</data>
<data alias="picture">1200</data>
</node>
<node id="1207">
<data alias="name">John</data>
<data alias="picture">1205</data>
</node>
<node id="1208">
<data alias="name">Michael</data>
<data alias="picture">1206</data>
</node>
</node>
I would like to have some XSLT, that ramdomly took one of the nodes id's and put it into a variable called "choosenNode".
Like this, if the node with the ID of 1207 was the selected one:
<xsl:variable name="choosenNode" value="1207" />
How can i do this? Is there a random-function in XSLT?
By the way, I would like the variable to be refreshed on every page where the XSLT is included.
And I work in Umbraco CMS, if that helps you guys.
Thanks,
-Kim
In Umbraco you can do something like this:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:Stylesheet [ <!ENTITY nbsp " "> ]>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxml="urn:schemas-microsoft-com:xslt"
xmlns:umbraco.library="urn:umbraco.library"
xmlns:Exslt.ExsltMath="urn:Exslt.ExsltMath"
exclude-result-prefixes="msxml umbraco.library Exslt.ExsltMath">
<xsl:output method="xml" omit-xml-declaration="yes"/>
<xsl:param name="currentPage"/>
<!-- This should probably be a macro parameter so you can use this elsewhere-->
<xsl:variable name="parentNode" select="1048"/>
<xsl:template match="/">
<xsl:variable name="numberOfNodes" select="count(umbraco.library:GetXmlNodeById($parentNode)/node)"/>
<xsl:variable name="randomPosition" select="floor(Exslt.ExsltMath:random() * $numberOfNodes) + 1"/>
<xsl:variable name="randomNode" select="umbraco.library:GetXmlNodeById($parentNode)/node [position() = $randomPosition]"/>
<!--
You now have the node in the $randomNode variable
If you just want the id then you can do an XPath query on the variable
or you can modify the XPath above to get the property you are after rather than
the whole node
-->
<xsl:value-of select="$randomNode/#nodeName" />
</xsl:template>
</xsl:stylesheet>
Hope this helps.
Tim
Getting random number in xslt is not an easy task.
There's something that can do it but you probably has to provide seed for random generator
http://fxsl.sourceforge.net/articles/Random/Casting%20the%20Dice%20with%20FXSL-htm.htm
Maybe the processor you are using to do xsl transformation has ability to extend xsl expressions with outside functions. In that case maybe you can use outside random function.
All you need is a random number generator. There is none in XSLT thus the random number must be provided by something outside of XSLT. You will need to call a method from an external library to do this and the implemention of this library will depend on if you're on Windows (.NET or WIN32) or Linux and the XSLT processor.
XSLT can do math but it lacks a lot of date/time related functions which happens to include a random number generator.
However, XSLT does have a XPath function called generate-id() which will generate an unique ID. If you could transform this to a nuimber somehow, it might be used to create a random number, although it would be predictable and some numbers might occur more often than others. I wouldn't use it.
If you use MSXSL to process your stylesheet then you can include JavaScript to generate random numbers within the stylesheet. (Or C# script when using .NET.)
Getting the node itself is easy, once you know the number of child nodes. Just ask for the node at the random position. Something like /node/node[5] would return the 5th node.
This solution works in a shell script that uses xsltproc and text utilities.
RandomElement=$(xsltproc style.xsl file.xml | tr ' ' '\n' | sort -uR | head -n 1)
It's assumed that the style.xsl file will select the required element set and return it's values, one per line in the output text file. The tr command should put each element onto a separate line. The sort -uR should produce a unique, random list of the elements selected by the style.xsl style sheet commands. The head -n 1 then pulls out the first line of the unique, random list.
The following assumes the XSLT processor supports EXSLT extensions (e.g., xsltproc).
This will return the content of the randomly selected "node" (it must be a child of a "node", i.e. a "node/node" element).
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:math="http://exslt.org/math"
extension-element-prefixes="math" >
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="node">
<xsl:variable name='selected'>
<xsl:value-of select="ceiling(math:random() * count(node))"/>
</xsl:variable>
<xsl:copy-of select="node[position() = $selected]"/>
</xsl:template>
</xsl:stylesheet>
This might be a useful snippet to process the content of the selected node:
<xsl:variable name="randomNode" select="node[position() = $selectNode]"/>
<id><xsl:value-of select="$randomNode/#id"/></id>
<name><xsl:value-of select="$randomNode/data[#alias='name']"/></name>
<picture><xsl:value-of select="$randomNode/data[#alias='picture']"/></picture>
Note that the above does not return the xslt definition of the variable, it uses that variable to copy the selected node.
To set the 'value' attribute of an xsl:variable element, try an attribute template like:
<xsl:variable name='chosenNode' value='{node[position() = $selected]/#id}'/>