Default namespacing XSLT... a weird learning experience

Kenny and I ran into an interesting debacle yesterday while attempting to fix a bug. This debacle dealt with the use of XML namespaces and XSL transforms. Even though both of us are proficient with XSL, we still found ourselves in a 2 hour bug fix over what eventually turned out to be a simple namespacing problem.

The rule is... if an XML document has a default namespace it must be matched in XSL by prefixing.

Typically, when we work with XML and XSL transforms, we strip the namespace of the XML before processing in XSL. This is more done for reasons of simplicity in that the transforms are performed on the server side with XML documents generated by serializing C# objects. It works, it's not a big deal.

In this instance, we happened to be grabbing SOAP from an ASP.NET Web Service for account objects and attempting to perform a client side transform in Mozilla Firefox 3.5.

Now the problem we were having was that the transform was not recognizing the root element. Previously, the template used a wildcard match since the WebService was outputing a DataTable (yuck).

We change the output to show a List of Account objects and POOF things were no longer working. My initial speculation was that the namespace of the XML root, http://www.ramsaycorp.com needed to be added to the XSL document. So we did this to no avail. After messing with it for a while, we finally realized that we had two solutions that worked:

1) Remove the default namespace from the XML document
2) Create a namespace prefix for http://www.ramsaycorp.com in the XSL document and reference Account elements with this prefix.

Ultimately, we opted for option #2. Call us namespace n00bs. The failure of XSL to utilize a default namespace threw us for a loop. Further testing was done in the .NET transform engine and it did not work when a default namespace was set for the XSLT.

Below is the XML output from the WebService, notice the default xmlns of http://www.ramsaycorp.com

<?xml version="1.0" encoding="utf-8"?>
<ArrayOfAccount xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.ramsaycorp.com">
  <Account>
    <Id>1</Id>
    <Parent>0</Parent>
    <TemplateID>1</TemplateID>
    <Company>Ramsay Corporation</Company>
    <Suspended>false</Suspended>
    <Header>defaultheader.htm</Header>
    <Footer>defaultfooter.htm</Footer>
    <Referred>Unknown</Referred>
    <PrimaryContact>0</PrimaryContact>
    <BillingContact>0</BillingContact>
    <ShippingContact>0</ShippingContact>
    <Division />
    <Location />
    <Info>
      <string />
      <string />
      <string />
    </Info>
    <History>RamsayCorp</History>
    <Utility>true</Utility>
    <HasNotes>true</HasNotes>
  </Account>
  <Account>
    <Id>17</Id>
    <Parent>1</Parent>
    <TemplateID>1</TemplateID>
    <Company>Sample Testers - 17</Company>
    <Suspended>false</Suspended>
    <Header>defaultheader.htm</Header>
    <Footer>defaultfooter.htm</Footer>
    <Referred>Unknown</Referred>
    <PrimaryContact>0</PrimaryContact>
    <BillingContact>0</BillingContact>
    <ShippingContact>0</ShippingContact>
    <Division />
    <Location />
    <Info>
      <string />
      <string />
      <string />
    </Info>
    <History />
    <Utility>true</Utility>
    <HasNotes>false</HasNotes>
  </Account>
</ArrayOfAccount>

XSL Template is shown below. The failure of trying to set the default namespace to http://www.ramsaycorp.com should have been obvious considering there are HTML elements in this template:

<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="1.0"
               xmlns:xsl="http://www.w3.org/1999/XSL/Transform"                
               xmlns:r="http://www.ramsaycorp.com"                
               >  
 
  <xsl:template match="r:ArrayOfAccount">  
   
    <table style="width: 100%" class="list">
      <thead>
        <tr>
          <td colspan="5">
            Search Results:
          </td>
        </tr>
        <tr>
          <td style="width: 50px;">ID</td>
          <td style="">Company</td>
          <td style="width: 100px;">Location</td>
          <td style="width: 100px;">Division</td>
          <td></td>
        </tr>
      </thead>
      <tbody>
        <xsl:if test="count(r:Account)=0">
          <tr>
            <td colspan="4">
              No accounts found matching this criteria.
            </td>
          </tr>
        </xsl:if>
        <xsl:apply-templates select="r:Account"></xsl:apply-templates>
      </tbody>
    </table>
  </xsl:template>

  <xsl:template match="r:Account">
    <tr onclick="editor.selectAccount({r:Id});" style="cursor: pointer;">
      <xsl:attribute name="class">
        <xsl:choose>
          <xsl:when test="position() mod 2 = 0">even</xsl:when>
          <xsl:when test="position() mod 2 = 1">odd</xsl:when>
        </xsl:choose>
      </xsl:attribute>
      <td>
        <xsl:value-of select="r:Id"/>
      </td>
      <td>
        <xsl:copy-of select="r:Company"/>
      </td>
      <td>
        <xsl:value-of select="r:Location"/>
      </td>
      <td>
        <xsl:value-of select="r:Division"/>
      </td>
      <td>

      </td>
    </tr>
  </xsl:template>

</xsl:stylesheet>

Comments

#1 Anonymous

Right away I am going away to do my breakfast, once having my breakfast coming
over again to read additional news.

Review my web blog :: euro,

Recent comments