Friday, January 19, 2018

Replace Namespace with XSLT in OSB

Recently, I'm working on a project and the vendor changed their framework for implementing their web services. This change caused their web service cannot fully compatible with existing WSDLs and the second level element will not have a correct namespace as before.

Before the change, the request will like below.
<ns0:getRelatedContacts xmlns:ns0="http://ws.crm.victor.com" xmlns:ns1="http://lib.ws.victor.com">
    <ns0:contactRequest>
        <ns1:FAID>FAID_1</ns1:FAID>
        <ns1:subscriptionId>subscriptionId_1</ns1:subscriptionId>
    </ns0:contactRequest>
</ns0:getRelatedContacts>

After the vendor change, the request will like below. The elment contactRequest lost its namespace.
<ns0:getRelatedContacts xmlns:ns0="http://ws.crm.victor.com" xmlns:ns1="http://lib.ws.victor.com">
    <contactRequest>
        <ns1:FAID>FAID_1</ns1:FAID>
        <ns1:subscriptionId>subscriptionId_1</ns1:subscriptionId>
    </contactRequest>
</ns0:getRelatedContacts>

The response has the same issue and second level element will not have a namespace. Due to the vendor cried they might take too much effort to fix it (I don't think it is true. :)), our team has to provide a solution to fix and make the service behavior we exposed to clients same as before and hence we need to find out a way to fix the namespace issue.

Definitely, there isn't a out-box solution. We have to create some code by ourselves to resolve this specific issue.

For replacing namespace, it is pretty simple with XSLT and can be done by a element copy. The next issue is to find out the second level element in the request to remove the namespace and adding the namespace back in response.

Here are the code for removing namespace from request.
<xsl:stylesheet version="1.0"  xmlns:xsd="http://www.w3.org/2001/XMLSchema"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
   
   <xsl:template match="/">
    <xsl:apply-templates/>
  </xsl:template>
  
  <xsl:template match="*[count(ancestor::node())=2]">
    <xsl:element name="{local-name()}">
      <xsl:copy-of select="@*"/>
      <xsl:apply-templates select='@*|node()'/>
    </xsl:element>
  </xsl:template>

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

Here are the code for adding namespace back in response.
<xsl:stylesheet version="1.0"  xmlns:xsd="http://www.w3.org/2001/XMLSchema"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
      
   <xsl:template match="/">
    <xsl:apply-templates/>
  </xsl:template>
  
  <xsl:template match="*[count(ancestor::node())=2]">
    <xsl:element name="{local-name()}" namespace="{namespace-uri(parent::node())}">
      <xsl:copy-of select="@*"/>
      <xsl:apply-templates select='@*|node()'/>
    </xsl:element>
  </xsl:template>

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

The above codes were tested in OSB 11g and 12c. They worked as what I expected.

No comments: