views:

133

answers:

1

I have a SOAP web service that is defined contract-first--the request and response xml are defined by xsds that use a number of different namespaces, and there are 100s of elements defined in xsds. However the web service calls a legacy layer which does not use namespaces in the xml. Therefore I have a transformation layer between the web service and the legacy layer which uses xslt to transform the request and response xml. On the way in the tranformation layer uses an xslt to strip namespace prefixes from the request xml, which is working OK, because there are only a small number of namespace prefixes to match against.

However on the way out I need an xslt that will add the namespace prefixes back into the response, and I am not sure how to do this. The response may consist of dozens of different element types; which may belong to one of several different namespaces in the xsds. For example, I may have a response like this:

<order>
  <item name="table"/>
  <customer name="jim"/>
</order>

I need to transform this into:

<order 
  xmlns:types1="http://types1.company.com" xmlns:types2="http://types2.company.com"&gt;
  <types1:item name="table"/>
  <types2:customer name="jim"/>
</order>

Is the only way to do this is to have a big table in the xslt that matches the element name in the response (e.g., "item", "customer") against the prefix that should be used?

Or would it be better to right some code that loads in the xsd as xml, and then matches the response elements to the elements in the xsd and derives the correct namespace that way?

+1  A: 

I think something like this could do the job:

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

  <!-- the identity template to copy everything as it is -->
  <xsl:template match="node() | @*">
    <xsl:copy>
      <xsl:apply-templates select="node() | @*" />
    </xsl:copy>
  </xsl:template>

  <!-- elements are re-created with a namespace -->
  <xsl:template match="*">

    <xsl:variable name="ns-uri">
      <xsl:choose>
        <xsl:when test="name() = 'item' and name(..) = 'order'">
          <xsl:text>http://types1.company.com&lt;/xsl:text&gt;
        </xsl:when>
        <xsl:when test="name() = 'customer' and name(..) = 'order'">
          <xsl:text>http://types2.company.com&lt;/xsl:text&gt;
        </xsl:when>
        <!-- otherwise: nothing -->
      </xsl:choose>
    </xsl:variable>

    <!-- create the element with the correct namespace -->    
    <xsl:element name="{name()}" namespace="{$ns-uri}">
      <xsl:apply-templates select="node() | @*" />
    </xsl:element>
  </xsl:template>

</xsl:stylesheet>

Output for me is:

<order>
  <item name="table" xmlns="http://types1.company.com"&gt;&lt;/item&gt;
  <customer name="jim" xmlns="http://types2.company.com"&gt;&lt;/customer&gt;
</order>

Which is what you have, just without the prefixes. The infoset is exactly the same.

Tomalak
That looks promising. I have created another variable to hold the prefix, and another xsl:choose to set up the prefix depending on the matching name().
ShellShock