tags:

views:

264

answers:

3

I can do it, but not for the default namespace, using the <xsl:namespace>. If I try to do it for the default namespace:

<xsl:namespace name="" select"myUri"/>

it never works. It demands that I explicitly define the namespace of the element to be able to use the above null prefix declaration.
The reason I want this is because I have a task to transform an input XML file to another output xml. The output XML has many elements and i dont want to have to explicitly set the namespace for every element. Thats why I want to set the default and never bother again. But the default must be computed from some data in the source XML. It does not change during the whole transformation, but it is dependent on input XML data. Any solution?

EDIT 1: To sup up:

  1. I want to create a namespace dynamically and set it to be the default namespace of the output xml document. The uri of the namespace is derived from some data in the input XML.
  2. If I use <xsl:namespace> in my root output element, I cannot create a default namespace for it, only a prefixed one. And even with the prefixed one, it does not propagate to children.

EDIT 2: dkackman proposed:

<xsl:template match="root">
  <xsl:param name ="ns">my-computed-namespace</xsl:param>
  <xsl:element name="newRoot" namespace="{$ns}"/>
</xsl:template>

It almost solves the problem. Unfortunately the children are injected with ""(blank) namespace by the transformer. Here is what I get if I put a child element:

<newRoot xmlns="my-computed-namespace"> 
    <child xmlns=""> ... 
    </child> 
 </newRoot>

Why does the transformer put this xmlns="" in the children? If I can prevent this then I have found my solution.

+1  A: 

Simple:

<xsl:stylesheet
 version="1.0"
 xmlns:xsl="..."
 xmlns="default output namespace for unprefixed elements"
>
  <!-- ... -->
</xsl:stylesheet>
Tomalak
The namespace must be dynamically computed from data in source XML. So I cant do it there.
Paralife
Have you considered to use two stylesheets? The first one extracts the namespace you are interested in and as its output creates a second stylesheet with the xmlns="..." as Tomalak has suggested, then you run the second stylesheet on the original input.
Martin Honnen
I d' love to do it, but the application i use it for demands one xsl file and i am not controlling it. Can I do this double-pass with one xsl file?
Paralife
+2  A: 

In addition to @Tomalak who has provided the precise answer, do note that <xsl:namespace> is not intended to create a namespace declaration to be used by the XSLT processor generally with all elements or attributes.

The purpose of <xsl:namespace> is to create a specific namespace node. This node has a limited scope only: the current element or attribute, and all children of the current node, if they do not re-assign the prefix to another namespace-uri.

Using <xsl:namespace> is necessary only if we want to create a namespace dynamically for a namespace-uri that must be generated dynamically (was not known statically at the start of the transformation). Such cases are extremely rare.

In all cases when the desired namspace-uri is known statically, simply declare this namespace at a suitable level of visibility (usually at the <xsl:stylesheet> instruction) and then simply use the associated prefix, anywhere this namespace must be used.

UPDATE: I have just confirmed in a dialog with specialists in another forum that this is not possible to do with <xsl:namespace>. It adds a namespace node with no name to the current element, but literal result elements are copied 1:1 and remain in their (no) namespace.

Here is how Dr. Michael Kay, the Editor of the W3C WG on XSLT explains this:

"You need to create elements and attributes with the correct expanded name at the time you create them. If that means using xsl:element, so be it. xsl:namespace can only be used to create additional namespace nodes to those that are created automatically for the prefixes/uris used in element and attribute names; it can't be used to modify the name of an element or attribute node. As always, to understand this you need to understand the data model for namespaces. An element/attribute name is a triple, containing (prefix, uri, localname). A namespace node is a pair (prefix, uri). There is a consistency rule that if an element or attribute name exists containing prefix=P uri=U then there must be a namespace node (P, U). The namespace fixup process ensures that this namespace node is created automatically when you create an element or attribute. xsl:namespace is there to allow you to create additional namespace nodes, typically for namespaces used in QName-valued content".

If such result is needed, the solution is to use a second pass and to convert any element belonging to "no namespace" to the desired new namespace.

This is the transformation to use in the second pass (the two passes can be combined into a single stylesheet/transformation):

<xsl:stylesheet  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;

  <xsl:variable name="vUrl" select="'my:Url'"/>

 <xsl:template match="*[namespace-uri()='']">
   <xsl:element name="{name()}" namespace="{$vUrl}">
     <xsl:copy-of select="@*"/>
     <xsl:apply-templates/>
   </xsl:element>

 </xsl:template>
</xsl:stylesheet>

When the above transformation is applied on the following sample (pass-1-result) xml document:

<a>
  <b>
    <c/>
  </b>
</a>

The desired result is produced:

<a xmlns="my:Url">
    <b>
        <c/>
    </b>
</a>
Dimitre Novatchev
But as I say in the question title this is exactly my problem: I want the namespace to be created dynamically, and I want it to set it to be the default namespace in the target output XML. See my update
Paralife
Thanks. I ll consider using it. Currently I decided to use the namespace="{$myNS}" attribute in every element declaration. It is cumbersome but i did it. But I understand that your solution is the only one that leaves my original xsl intact (except the second pass), so i consider it as accepted answer
Paralife
Also, out of curiosity, why does the namespace attribute of parent gets automatically overriden with blank in the children(if i omit namespace in the children) instead of being propagated? Is the specific engine i use or it is a rule of xslt? (I use altova)
Paralife
@Paralife The children are not *originally* children. They only become children in the result document. They are *copied* into the result document with all their namespace nodes and do not inherit namespace nodes from an unknown node (what would become their parent) during this copying.
Dimitre Novatchev
Perfectly clear. But then, when copying those elements for which there is no explicit namespace attribute in the xslt why put xmlns="" in the result? (As opposed to just copy them without putting the xmlns="") They could silently inherit their result parent namespace in the result document
Paralife
Hmm I think I can answer my own question: Beacause when copying those elements for which there is no namespace attribut explicitly declared, the xslt uses the default namespace defined in the stylesheet, and since i do not have a default namespace declared in xsl:stylesheet(because i dont know it beforehand, it is dependent on source xml) the xslt uses ""...
Paralife
So the best solution would be to have the transformer work like this: For elements not explicitly created with namespace attribute use the default namespace if it is declared and if it is not declared then do not put namespace in the result element instead of putting xmlns="". Is this behaviour defined by standards or is it the altova that i use?
Paralife
A: 

Try this:

<xsl:template match="root">
  <xsl:param name ="ns">my-computed-namespace</xsl:param>
  <xsl:element name="newRoot" namespace="{$ns}"/>
</xsl:template>

Then you can call that like so:

    <xsl:apply-templates select="root">
      <xsl:with-param name="ns" select="computationXPath"/>
    </xsl:apply-templates>

EDIT Or with a variable instead of a param:

  <xsl:variable name ="ns">my-computed-namespace</xsl:variable>
  <xsl:template match="root">
    <xsl:element name="newRoot" namespace="{$ns}">
       <xsl:apply-templates/>
    </xsl:element>
  </xsl:template>

(no need to call templates with-param)

dkackman
I ve already tried this and it almost works because the output in the root element is correct but it explicitly nulls the namespace of the children. The output is this: <newRoot xmlns="my-computed-namespace> <child xmlns=""> ... </child></newRoot>
Paralife
Then I see three options: 1) explicitly call all templates with the ns param (like above)2) Implement a two pass transformation - first generate the stylesheet so that the computed namespace can be included normally. and then use the generated stylesheet to transform your input data.3) Use an extension object
dkackman
Yeah, these are the solutions I wanted to avoid... I guess I ll have to compute the namespace value in a stylesheet wide xsl:variable and go to every xsl:element declaration and either attach a namespace="{$ns}" attribute, or declare a xsl:namespace in it and prefix the output with the prefix.
Paralife