views:

446

answers:

3

Hi

I want to go from this :

<detail> 
 <BusinessError> 
   <environment>Test</environment> 
   <businesserror> 
      <BusinessCode>my_businesscode</BusinessCode> 
      <BusinessMessage>my_businessmessage</BusinessMessage> 
   </businesserror> 
  </BusinessError> 
 </detail> 

To this (only the businesserror element is of interest)

<detail> 
 <v1:BusinessError  xmlns:v1="ddd"> 
   <v1:environment>Test</v1:environment> 
   <v1:businesserror  xsi:type="ns2:AppErrorType" xmlns:ns2="aaaaa" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt; 
     <ns2:BusinessCode>my_businesscode</ns2:BusinessCode> 
     <ns2:BusinessMessage>my_businessmessage</ns2:BusinessMessage> 
   </v1:businesserror> 
 </v1:BusinessError> 
</detail>

Important detail (I think) the namespace uri's are imported as a variable !

I can add the xsi:type via xs:attribute and the xsi namespace is added automatically but not the namespace (xmlns:ns2="aaaaa") ? notice that the namespace of the containing element (businesserror) is different (ddd) from the one I try to declare (aaaa)

thanks

A: 

This XSLT will add the namespace definitions to the root element (detail).

<?xml version="1.0" encoding="utf-16"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                version="1.0"
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                xmlns:ns2="aaaaa" xmlns:v1="ddd">
  <xsl:output omit-xml-declaration="yes" method="xml" version="1.0" />
  <xsl:template match="/">
    <xsl:apply-templates select="/detail" />
  </xsl:template>
  <xsl:template match="/detail">
    <detail>
      <xsl:element name="v1:BusinessError">
        <xsl:element name="v1:environment">
          <xsl:value-of select="BusinessError/environment/text()" />
        </xsl:element>
        <xsl:element name="v1:businesserror">
          <xsl:attribute name="xsi:type">ns2:AppErrorType</xsl:attribute>
          <xsl:element name="ns2:BusinessCode">
            <xsl:value-of select="BusinessError/businesserror/BusinessCode/text()" />
          </xsl:element>
          <xsl:element name="ns2:BusinessMessage">
            <xsl:value-of select="BusinessError/businesserror/BusinessMessage/text()" />
          </xsl:element>
        </xsl:element>
      </xsl:element>
    </detail>
  </xsl:template>
</xsl:stylesheet>

Do not change the <detail> node to an xsl:element node as this will not transfer over the namespace definitions.

Vod
A: 

If you want the namespace to be dynamically created then I think you need to use XSLT 2.0 with its xsl:namespace element:

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="2.0">

  <xsl:param name="v1" select="'ddd'"/>
  <xsl:param name="ns2" select="'aaaa'"/>

  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>

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

  <xsl:template match="businesserror">
    <xsl:element name="v1:{local-name()}" namespace="{$v1}">
      <xsl:attribute name="xsi:type" namespace="http://www.w3.org/2001/XMLSchema-instance" select="'ns2:AppError'"/>
      <xsl:namespace name="ns2" select="$ns2"/>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:element>
  </xsl:template>

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

</xsl:stylesheet>

With XSLT 1.0 you would need to hardcode the namespaces in the stylesheet, to copy them over:

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:v1="ddd"
  xmlns:ns2="aaaa"
  version="1.0">

  <xsl:variable name="ns" select="document('')/xsl:stylesheet/namespace::*"/>

  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="BusinessError | environment">
    <xsl:element name="v1:{local-name()}" namespace="{$ns[local-name() = 'v1']}">
      <xsl:apply-templates select="@* | node()"/>
    </xsl:element>
  </xsl:template>

  <xsl:template match="businesserror">
    <xsl:element name="v1:{local-name()}" namespace="{$ns[local-name() = 'v1']}">
      <xsl:copy-of select="$ns[local-name() = 'ns2']"/>
      <xsl:attribute name="xsi:type" namespace="http://www.w3.org/2001/XMLSchema-instance"&gt;ns2:AppError&lt;/xsl:attribute&gt;      
      <xsl:apply-templates select="@* | node()"/>
    </xsl:element>
  </xsl:template>

  <xsl:template match="BusinessCode | BusinessMessage">
    <xsl:element name="ns2:{local-name()}" namespace="{$ns[local-name() = 'ns2']}">
      <xsl:apply-templates select="@* | node()"/>
    </xsl:element>
  </xsl:template>

</xsl:stylesheet>
Martin Honnen
A: 

If you know your XSLT 1.0 processor supports an extension function like exsl:node-set then you can do it as follows:

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:exsl="http://exslt.org/common"
  version="1.0">

  <xsl:param name="v1" select="'ddd'"/>
  <xsl:param name="ns2" select="'aaaa'"/>

  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>

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

  <xsl:template match="businesserror">
    <xsl:variable name="t1">
      <xsl:element name="ns2:foo" namespace="{$ns2}"/>
    </xsl:variable>
    <xsl:element name="v1:{local-name()}" namespace="{$v1}">
      <xsl:copy-of select="exsl:node-set($t1)/*/namespace::*[local-name() = 'ns2']"/>
      <xsl:attribute name="xsi:type" namespace="http://www.w3.org/2001/XMLSchema-instance"&gt;ns2:AppError&lt;/xsl:attribute&gt;
      <xsl:apply-templates select="@* | node()"/>
    </xsl:element>
  </xsl:template>

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

</xsl:stylesheet>
Martin Honnen
Works like a charm - thank you very much
Danny