views:

113

answers:

3

I think the best way to ask this question is: How do I specify a default namespace for the root element in the output? Doing this:

<xsl:template match="/">
        <r xmlns:s"http://www.mycompany.com/s/schema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.mycompany.com/default/schema" >
....
....

Gives me an error in Oracle:

ORA-31011: XML Parsing Failed
ORA-19201: Error occurred in in XML Processing
LPX-00604: Invalid attribute 'nIfNotExist', for attribute 'name'
ORA-06512: at SYS.XMLType at line 74 
ORA-06512: at line 24

where the 'nIfNotExist' is a template:

 <xsl:template name="nIfNotExist" xmlns:scom="http://www.mycomapny.com/s/schema"&gt;
  <xsl:param name="nodeToTest"/>
  <xsl:param name="nodeName"/>
                ...

I want the resulting document to have the root element look like this:

<r xmlns:s="http://www.mycompany.com/s/schema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.mycompany.com/default/schema"&gt;

I want "http://www.mycompany.com/default/schema" as the default namespace so the document can pass XSD validation. Otherwise, I have to add it manually before running validation (not an option for batch processing).

EDIT

I have tried this:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:s="http://www.mycompany.com/schema"
 xmlns="http://www.mycompany.com/def_schema"&gt;

The result is a document with no data, like this:

<r xmlns:s="http://www.mycompany.com/schema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.mycompany.com/def_schema"&gt;
    <a></a>
    <s:b></s:b>
    <c></c>
    ....

It should have been:

<r xmlns:s="http://www.mycompany.com/schema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.mycompany.com/def_schema"&gt;
    <a>123</a>
    <s:b>ABC34L</s:b>
    <c>7.092381</c>

UPDATE

Source data looks something like this (input that I get has no namespaces defined in it):

<ROOT_NODE>
    <DATA_A>1234</DATA_A>
    <DATA_B>34567</DATA_B>
    <OTHER_DATA_C>7.123456</OTHER_DATA_C>
</ROOT_NODE>

Desired output

<r xmlns:s="http://www.mycompany.com/schema"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
   xmlns="http://www.mycompany.com/def_schema"&gt;
    <a>1234</a>
    <s:b>34567</s:b>
    <c>7.123456</c>
</r>
A: 

You're halfway to the right answer. When you declare a default namespace at the root of the transform, you are asserting (assuming that you don't override the declaration elsewhere) that all of the non-qualified elements in that document belong to that namespace. Every non-qualified element that the transform emits will belong to that namespace. You've got that part right.

What I think you're probably overlooking is that namespace declarations in an XSLT transform also apply to the XPath patterns in the transform. I'd bet that the XPath node tests in your transform aren't matching any of the input nodes because the input nodes are in the empty namespace, not the namespace you've declared in the transform.

What you probably want to do is something like this:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 xmlns:s="http://www.mycompany.com/schema" 
 xnlns:no=""
 xmlns="http://www.mycompany.com/def_schema"&gt;

...and then change the patterns in the transform accordingly:

<xsl:template match="no:foo">
   <foo>...</foo>
</xsl:template>

This is, by the way, one of the reasons that xsl:element exists - you can build a template that transforms elements from one namespace to another with it, e.g.:

<xsl:template match="no:*">
   <xsl:element name="{local-name()}">
      <xsl:apply-templates select="node()|@*"/>
   </xsl:element>
</xsl:element>

Edit:

I haven't played around with namespace in quite this way before, and it turns out that the above is not actually legal. You can't specify no namespace with a namespace prefix. So you can't use the target namespace as the default namespace of your transform, because then you have no way of telling XPath to find the elements in the source document.

You can either specify the output namespace with a prefix, e.g.:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 xmlns:s="http://www.mycompany.com/schema" 
 xmlns:out="http://www.mycompany.com/def_schema"&gt;

...but that will write the out namespace prefix to the output, which won't bother any XML processor but might bother humans. Or you can specify it explicitly in your templates, e.g.:

<xsl:template match="foo">
   <xsl:element name="foo" namespace="http://www.mycompany.com/def_schema"&gt;
      ...
   </xsl:element>
</xsl:template>
Robert Rossney
The trick with `xmlns:no=""` causes Oracle to say "LPX-00284: namespace prefix to NULL URI is not allowed". Also, PL/SQL Developer doesn't allow it either, saying that only a default namespace can be null (I had cheat - I had to load it from a text editor rather than the XML editor).
FrustratedWithFormsDesigner
Your second suggestion, might work... I got a default namespace appearing on the root node in the output! The only problem is... I still need the declarations of the other namespaces (my `s` namespace and the `xsi` namespace). It looks like Oracle doesn't support the `xsl:namespace` element... Assuming Oracle only (safely) supports XSLT 1.0, how can I have multiple namespaces declared on an element?
FrustratedWithFormsDesigner
If your transform emits any nodes in those two namespaces, XSLT will add the namespace declarations. If not, it won't. But you shouldn't need those namespace declarations if there aren't any nodes in those namespaces.
Robert Rossney
Also: Oracle's not to blame for that LPX-00284 error, I am. XML doesn't allow you to associate a namespace prefix with no namespace, and I should have tested that before blithely telling you it would work.
Robert Rossney
+2  A: 

This transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:s="http://www.mycompany.com/schema"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns="http://www.mycompany.com/def_schema"
 >
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/*">
  <r>
   <xsl:apply-templates/>
  </r>
 </xsl:template>

 <xsl:template match="DATA_A">
  <a>
   <xsl:apply-templates/>
  </a>
 </xsl:template>

  <xsl:template match="OTHER_DATA_C">
   <c>
    <xsl:apply-templates/>
   </c>
  </xsl:template>

  <xsl:template match="DATA_B">
   <s:b>
    <xsl:apply-templates/>
   </s:b>
  </xsl:template>
</xsl:stylesheet>

when applied on the provided XML document:

<ROOT_NODE>
    <DATA_A>1234</DATA_A>
    <DATA_B>34567</DATA_B>
    <OTHER_DATA_C>7.123456</OTHER_DATA_C>
</ROOT_NODE>

produces the wanted result:

<r xmlns:s="http://www.mycompany.com/schema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.mycompany.com/def_schema"&gt;
    <a>1234</a>
    <s:b>34567</s:b>
    <c>7.123456</c>
</r>
Dimitre Novatchev
Hmm, I tried this in Oracle (Exact input and XSLT as here) and all it produced was `<r xmlns="http://www.mycompany.com/def_schema">1234345677.123456</r>` (Oracle 10g) At least it got the default namespace right. Same technique didn't work on the main document, though the main document only has one template for root element then all other transforms under that.
FrustratedWithFormsDesigner
It appears to be concatenating the contents of `DATA_A`, `DATA_B`, and `OTHER_DATA_C`, and making that the content of `r`.
FrustratedWithFormsDesigner
@FrustratedWithFormsDesigner: Then either you dont have the same XML as the one you provided, or ORacle's XSLT processor that you are using has some bug.
Dimitre Novatchev
@Dimitre: The results I described are based on your transformation in this post and the copy of the sample input I described.
FrustratedWithFormsDesigner
+1 This is right.
Alejandro
@Alejandro: It probably is. I haven't confirmed an *actual* bug in Oracle (10.2), though it seems that's what I'm facing. To make things more fun, it looks like Oracle's XSLT processor follows XSLT 1.0 and some small parts of 2.0. So, I think at this point I need an Oracle-specific workaround for what should have been pretty easy to solve.
FrustratedWithFormsDesigner
@FrustratedWithFormsDesigner: I've tested with Oracle XSLT Processor v10. Result in correct output.
Alejandro
@Alejandro: Ok, clearly I'm missing something. Could you post the code? I just can't seem to get this working the way I think it should be. :/
FrustratedWithFormsDesigner
@FrustratedWithFormsDesigner: Just this command line `java -classpath "xmlparserv2.jar" oracle.xml.parser.v2.oraxsl input.xml transform.xsl output.xml`
Alejandro
@Alejandro: My code is supposed to be executed in PL/SQL.
FrustratedWithFormsDesigner
A: 

There are many possible solutions, but none seem to work well in Oracle, from PL/SQL. One of the other devs here "solved" this by converting the XML object to a CLOB, performing some string manipulation to force the default namespace into the root element, and then converting back to XML for the next step... I don't like it but it works...

FrustratedWithFormsDesigner