views:

134

answers:

3

Hello everybody,

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)

+1  A: 

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.

Welbog
For a nested local name match, would I need to do `*[local-name()='louis']/*[local-name()='weapon']/*[local-name()='auto-shotgun']` in place of `surv:louis/surv:weapon/surv:auto-shotgun`?
adam_0
Also, I'd really prefer a solution that transferred the namespace over to the resulting file instead of ignoring it or needing to know it ahead of time. (EDIT: I like the Left 4 Dead theme ;)
adam_0
Yes. It's very ugly, as you can see.
Welbog
A solution like that would depend on exactly what XSLT library you're using and how it handles namespaces, if it's even possible.
Welbog
I would prefer something that's library-neutral, if possible, but I am using Saxon to parse my XSLT.
adam_0
Because of the fact that namespace are used to identify elements, I don't think you'll be able to find a library-agnostic solution to this problem. At best you'll have to have an XSLT file that takes the namespace as a parameter and outputs another XSLT file that uses that namespace. Additionally, I'm not familiar with Saxon so I can't help you there either.
Welbog
Well thanks for the information and help... +1
adam_0
You're better off leaving this answer at 0 so your question stays in the unanswered list.
Welbog
Drat, my vote's locked... if you edit the question, I can un-upvote it. :/
adam_0
Something I just noticed that you said: QUOTE:"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." The idea of this project is to have XSDs / namespaces created on the fly, so it's just something I have to try to deal with.
adam_0
Couldn't I just treat the namespace URI as an attribute and retrieve it that way? I don't know what the code would be for that, but would that work?
adam_0
+2  A: 

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"&gt;
    <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"&gt;
    <xsl:template match="*">
        <xsl:copy>
            <xsl:apply-templates select="*[1]|following-sibling::*[1]"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>
Alejandro
Thanks, Alejandro. I forgot to mention that there is ONE and only ONE namespace for the entire file (like your first example). I just don't know what it's named or what URI it has. Is there a way of getting this from the input file?
adam_0
@adam_0: Yes. If you have only one default namespace declaration in the root element you could use `namespace-uri(/*)` as in my answer.
Alejandro
My apologies, I missed that... I'll try it out and report back on any progress.
adam_0
@adam: Also, if there is only one namespace for all the elements in the tree, then you'll only need `namespace-uri()` on a template for wich the context node is any element.
Alejandro
See question for update. It's not working :[
adam_0
@adam_0: check new edit and tell me if it's more clear now.
Alejandro
This didn't make sense to my situation. I'll re-edit my code to be closer to what I'm getting at...
adam_0
@adam_0: As my example is showing, the need for `xsl:element` depends on the kind of transformation. Regrettably, namespace declarations inside literal result element are not AVT, so this `<bar xmlns="{namespace-uri()}">` don't work.
Alejandro
I'm not talking about applying the namespace to already existing elements but new ones. I'm nesting the elements that I copy inside of some new elements (root, foo, and bar) and I need to apply the namespace to all of those. My method works but it is ugly, and I would like to have one statement that discusses the namespace, if possible. Is that possible?
adam_0
@adam_0: QNames are a tuple (Namespace Uri, Local Name, Prefix). When you use `<xsl:element name="bar"/>` or just `<bar/>` the tuple is something like ("","bar",""). There is no way to declare a dynamic binding bettween a prefix and a URI. In XSLT 2.0 there is an static way to declare a binding bettween a namespace URI and non prefix elements in xpath expressions. But that's not good for your case anyway.
Alejandro
There's no way I could do something like `<xsl:ns name="NAMESPACE"><xsl:element name="root">...` ? Where the `<xsl:ns>` tag would apply its namespace to all sub-elements?
adam_0
@adam_0: In any XML tree (stylesheet included) a namespace declaration is in scope for the parent element an its childs, unless other declaration overwrites it. That way everybody declare the `xmlns:xsl="..."` in the element root of the stylesheet. In XSLT 2.0, there is a `xsl:namespace` instruction that works more like `xsl:attribute`, but again there is no way for the processor to retain a prefix-to-uri binding from one template to other unless there is a **static** namespace declaration in some common ancestor.
Alejandro
A: 

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.

Gregory Higley
I know that I would be able to solve this problem with some other technology like XLinq, but sadly the only option that I have is using JUST XSLT.
adam_0
@adam_0: I think there is no ugliness in `local-name()` or `namespace-uri()` a `QName` is a tuple of (URI, local name, prefix). Also in a transformation pipeline you could use XSLT 2.0 @xsl:xpath-default-namespace
Alejandro