views:

349

answers:

4

I'm not sure if that is possible, as I'm very new to XSLT and stuff, but maybe some of you could help me here? It's a bit tricky and I haven't found anything like it on the internet:

The problem is that I have an input xml with namespaces declared and all and I only need to make slight changes to it (adding or deleting attributes, or shifting them to other locations). But at the same time, I have to update the namespace references in the document's document tag. So, for example, the input xml might look something like this:

<order
  xmlns="some.url.01"
  xmlns:ns2="some.other.url"
  xmlns:ns3="another.one"
>
  <orderEntry>
    <orderControl>
      <mandant>test</mandant>
      <businessUnit>test</businessUnit>
      <inboundChannel>test</inboundChannel>
      <timestamp>timestamp</timestamp>
      <requestedDocuments>
        <ns2:document>orderForm</ns2:document>
      </requestedDocuments>
    </orderControl>
  </orderEntry>
</order>

the resulting xml should look like this:

<order
  xmlns="some.url.02"
  xmlns:ns2="some.other.url.02"
  xmlns:ns3="another.one.02"
>
  <orderEntry>
    <orderControl>
      <mandant>test</mandant>
      <businessUnit>test</businessUnit>
      <inboundChannel>test</inboundChannel>
      <!-- deleted timestamp for example -->
      <requestedDocuments>
        <ns2:document>orderForm</ns2:document>
      </requestedDocuments>
    </orderControl>
  </orderEntry>
</order>

but the only thing I get is:

<order
  xmlns="some.url.02"
>
  <orderEntry>
    <orderControl>
      <mandant>test</mandant>
      <businessUnit>test</businessUnit>
      <inboundChannel>test</inboundChannel>
      <!-- deleted timestamp for example -->
      <requestedDocuments>
        <ns2:document xmlns:ns2="some.other.url.02">orderForm</ns2:document>
      </requestedDocuments>
    </orderControl>
  </orderEntry>
</order>

Now maybe for one or two of you it might not be that big a deal, but I have the restriction that the output document should look one-to-one the same as the input document except for the requested changes (namespace changes and deletion).

My XSLT looks a 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="some.url.02"
  xmlns:ns2="some.other.url.02"
  xmlns:ns3="another.one.02"
>
  <xsl:output method="xml" version="1.0" encoding="UTF-8" standalone="yes" indent="yes"/>

  <xsl:strip-space elements="*"/>

  <xsl:template match="*">
    <xsl:choose>
      <xsl:when test="name(.) != 'timestamp'">
        <xsl:element name="{node-name(.)}">
          <xsl:apply-templates select="@*|node()"/>
        </xsl:element>
      </xsl:when>
    </xsl:choose>
  </xsl:template>

  <xsl:template match="@*">
    <xsl:attribute name="{node-name(.)}">
      <xsl:value-of select="."/>
    </xsl:attribute>
  </xsl:template>
</xsl:stylesheet>

Can somebody please help? Namespaces are tricky :(

P.S.: Whoever edited my entry: Thanks :)

+1  A: 

You can set the namespace on the output element with the namespace attribute:

<xsl:element name="{node-name(.)}" namespace="http://www.bar.org"&gt;
  // ...
</xsl:element>

Note that the namespace must be a URI and although I expect you know this it's probably a good idea to use URIs in your example.

Here is a link to the excellent ZVON tutorial which has worked examples: http://www.zvon.org/xxl/XSLTreference/Output/xslt%5Felement%5Fnamespace.html

I agree that namespaces are tricky. As you know the prefix is semantically irrelevant, but many systems allow you to choose your prefix for aesthetic reasons. Also look at Saxon (http://saxon.sourceforge.net/)

EDIT I think you will find your answer here: http://stackoverflow.com/questions/885264/xslt-root-tag-namespace-instead-of-element-attribute-namespace

peter.murray.rust
You can store the URI in an `<xsl:variable/>` and use it like `namespace="{$uri}"`. That's quite nice if you need it more often.
Boldewyn
If I'm correct than this, too, would result in the same XML, having the namespaces in the tags using those namespaces, right?
Wickermoon
Thank you thousands of times! That link did actually help me, now it works, yay! =)
Wickermoon
A: 
<xsl:template match="a:*">
  <xsl:element name="{local-name()}"
               namespace="http://example.com/B"&gt;
    <xsl:copy-of select="@*" />
    <xsl:apply-templates />
  </xsl:element>
</xsl:template>

It searches for any element in namespace with prefix a and replaces it with an element with the same name of namespace http://example.com/B. All attributes are copied 'as is' and then all children are evaluated.

Add your custom processing in or around that as needed.

Boldewyn
@Boldewyn - don't you need to use an attribute-value template {...} for name?
peter.murray.rust
Unfortunately, when I add this to my XSLT, it creates the exact same xml as before, having the namespaces in each element with that namespace, instead of having the namespaces declared once in the root element (order in this case). =(
Wickermoon
@peter.murray.rust: oh, yes, you're right. Sorry, Wickermoon, I'll update my answer in a second.
Boldewyn
@Wickermoon: OK, now it does what it advertises. Take elements of namespace A as input and output synonymical elements in namespace B.
Boldewyn
Btw: This time I tested it in Saxon 9.something.
Boldewyn
+1  A: 
<xsl:stylesheet
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:ns1_src="some.url.01"
  xmlns:ns2_src="some.other.url"
  xmlns:ns3_src="another.one"
  xmlns="some.url.02"
  xmlns:ns2="some.other.url.02"
  xmlns:ns3="another.one.02"
>
  <!-- 
    Note that all the source namespaces got their own new "*_src" prefix. 
    The target namespaces take over the original prefixes. 
    "some.url.02" is the new global namespace.
  -->

  <xsl:output method="xml" version="1.0" encoding="UTF-8" standalone="yes" indent="yes"/>
  <xsl:strip-space elements="*"/>

  <!-- the identity template to copy everything, unless 
       it has been declared otherwise -->
  <xsl:template match="node() | @*">
    <xsl:copy>
      <xsl:apply-templates select="node() | @*" />
    </xsl:copy>
  </xsl:template>

  <!-- three templates to handle elements -->
  <xsl:template match="ns1_src:*">
    <xsl:element name="{local-name()}">
      <xsl:apply-templates select="node() | @*" />
    </xsl:element>
  </xsl:template>

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

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

  <!-- three templates to handle attributes -->
  <xsl:template match="@ns1_src:*">
    <xsl:attribute name="{local-name()}">
      <xsl:value-of select="." />
    </xsl:attribute>
  </xsl:template>

  <xsl:template match="@ns2_src:*">
    <xsl:attribute name="ns2:{local-name()}">
      <xsl:value-of select="." />
    </xsl:attribute>
  </xsl:template>

  <xsl:template match="@ns3_src:*">
    <xsl:attribute name="ns3:{local-name()}">
      <xsl:value-of select="." />
    </xsl:attribute>
  </xsl:template>

  <!-- timestamps will be ignored -->
  <xsl:template match="ns1_src:timestamp" />

</xsl:stylesheet>

Output:

<order xmlns="some.url.02">
  <orderEntry>
    <orderControl>
      <mandant>test</mandant>
      <businessUnit>test</businessUnit>
      <inboundChannel>test</inboundChannel>
      <requestedDocuments>
        <ns2:document xmlns:ns2="some.other.url.02">orderForm</ns2:document>
      </requestedDocuments>
    </orderControl>
  </orderEntry>
</order>
Tomalak
Thanks for the help, but I think I didn't explain it clearly enough, so give me another try:The resulting output you gave me is what I get, but what I want is the xmlns:ns2 declaration to be in the order tag. =/
Wickermoon
@Wickermoon - the new link in my answer may help you
peter.murray.rust
A: 

Are you using Ant's XSLT task to do your transformation?

If the answer is yes, you may want to switch from the default XSLT engine that comes with Sun JDK 1.5+. Read this.

Also, read this article about namespaces in XSLT

Alexander Pogrebnyak
Thanks for the links, but no, I actually use java and Saxon9.something to do the transformation. :)
Wickermoon