XML

Using the XML DOM to Work with XSLT and XSL

Since the XML DOM is capable of working with XSLT and XSL, we can write an application that uses the DOM to transform an XML document using either an XSLT or an XSL document. This application can be placed on the server and used by an ASP page. If you know that the client has the DOM installed, you can also use this component on the client. In our case, we'll write the application to work on the server.

When a document is requested, the ASP document will call the application, and then the application will transform the XML document using an XSL or XSLT document. After the XML document is transformed, the application will return the document to the ASP document, which will return the transformed document to the client. We will write the application in Microsoft Visual Basic.

To write the application, you must start by creating a new Visual Basic ActiveX DLL project. Name the project TransformXML, and name the default class Transform. You will need to get a reference to Microsoft XML version 2.6 and the MTS library. In Microsoft Windows 2000, this library will be the COM+ Services Type Library. In the Properties Window change the MTSTransactionMode property to 1 - No Transactions.

Now that Visual Basic has properties, you should use properties instead of global variables. Properties allow you to have more control over the variables in your project than using global variables. We will use properties in this example, even for private variables.

This example will use three document objects. The first document object, m_oDOMDocumentSource, will be used to get a reference to the XML document. The second document object, m_oDOMDocumentStylesheet, will be used to get a reference to the XSL or XSLT document. The third document object, m_oDOMDocumentResult, will hold the transformed XML document.

Add the following declarations and properties to the Visual Basic project named TransformXML.vbp:

  Option Explicit
  'Document Object for the source
  Private WithEvents m_oDOMDocumentSource As MSXML2.DOMDocument26
  'Document object for the XSL or XSLT document
  Private WithEvents m_oDOMDocumentStylesheet As MSXML2.DOMDocument26
  'Document object for transformed XML document
  Private m_oDOMDocumentResult As MSXML2.DOMDocument26
  'Variable used by StyleSheetURL property
  Private m_sStyleSheetURL As String
  'Variable used by ReturnHTML property
  Private m_sReturnHTML As String
  'Get a reference to the MTS Object Context Events
  Implements ObjectControl
  'Property to get and set StyleSheetURl property
  Private Property Get StyleSheetURL() As String
     StyleSheetURL = m_sStyleSheetURL
  End Property
  Private Property Let StyleSheetURL _
     (ByVal v_sNewStyleSheetURL As String)
     m_sStyleSheetURL = v_sNewStyleSheetURL
  End Property
  'Property to get and set return HTML
  Private Property Get ReturnHTML() As String
     ReturnHTML = m_sReturnHTML
  End Property
  Private Property Let ReturnHTML(ByVal v_sNewReturnHTML As String)
     m_sReturnHTML = v_sNewReturnHTML
  End Property

Now we will actually implement the objectControl interface:

  Private Sub ObjectControl_Activate()
     Set m_oDOMDocumentSource = New DOMDocument26
     Set m_oDOMDocumentStylesheet = New DOMDocument26
     Set m_oDOMDocumentResult = New DOMDocument26
  End Sub
  Private Function ObjectControl_CanBePooled() As Boolean
     ObjectControl_CanBePooled = False
  End Function
  Private Sub ObjectControl_Deactivate()
     Set oDOMDocumentSource = Nothing
     Set oDOMDocumentStylesheet = Nothing
     Set oDOMDocumentResult = Nothing
  End Sub

This application is complex because we must wait for the XML document and the XSL or XSLT document to be loaded before we can perform the transformation. We declare the m_oDOMDocumentSource and m_oDOMDocumentStylesheet with events to get the events of these objects. Using these events, we can trap when the document is actually loaded. When both documents are loaded, we can do the transformation and return the transformed document.

We will now add a function that will return the transformed XHTML string. This function can be called from an ASP page, or it can be called from any application. Add the following function to the project:

  Public Function ApplyXSLStyle(ByVal v_vXMLDocument As Variant, _
                     ByVal v_vXMLStyleSheet As Variant) As String
     On Error GoTo ApplyStyleError
     StyleSheetURL = v_vXMLStyleSheet
     m_oDOMDocumentResult.async = False
     m_oDOMDocumentSource.async = False
     m_oDOMDocumentStylesheet.async = False
     m_oDOMDocumentSource.Load (v_vXMLDocument)
     'Check for an error
     If m_oDOMDocumentSource.parseError.errorCode <> 0 Then
        DOMError
     End If
     'We will not get to this next line of code until the
     'document is loaded, and the code in
     'm_oDOMDocumentSource_onreadystatechange is executed.
     ApplyXSLStyle = ReturnHTML
     GetObjectContext.SetComplete
     Exit Function
     ApplyStyleError:
        Err.Raise Err.Number, Err.Source, _
           "The following error has occured:" & vbCrLf & _
           "Error Description: " & Err.Description & vbCrLf & _
           "Error Source: " & Err.Source & _
           "XSLXSLT:ApplyStyle" & vbCrLf & _
           "Error Number: " & Err.Number
  End Function

Once we call the load function, the next event fired is the onreadystatechange event. The onreadystatechange event is called four times. The first time the event is raised is when the document is loading. While the document is loading, the readystate variable of the IXMLDOMDocument object will be 1. The event is raised for a second time when the document is loaded but not yet in the document object. When the document is loaded but not in the document object, the readystate variable will have a value of 2. The event is raised for a third time when the document is loaded and partially loaded into the document object. When the document is partially loaded the readystate property will have a value of 3. The fourth time the event is raised the document is completely loaded into the document object and ready to use. When the document is fully loaded, the readystate property will be 4. Thus, using the onreadystatechange event and the readystate property, we can determine when the object is actually loaded.

Because we will need to handle any errors loading documents for three document objects, we have moved the error handler into a separate function called DOMError. Add the following into the project:

  Private Sub DOMError()
     Dim objXMLParseError As IXMLDOMParseError
     If m_oDOMDocumentResult.parseError.errorCode <> 0 Then
        Set objXMLParseError = m_oDOMDocumentResult.parseError
     End If
     If m_oDOMDocumentSource.parseError.errorCode <> 0 Then
        Set objXMLParseError = m_oDOMDocumentSource.parseError
     End If
     If m_oDOMDocumentStylesheet.parseError.errorCode <> 0 Then
        Set objXMLParseError = m_oDOMDocumentResult.parseError
     End If
     With objXMLParseError
        Err.Raise .errorCode, _
            "XMLStylesheet.XSLXSLT", _
             "The following error occured:" & vbCrLf & _
             "error code: " & .errorCode & vbCrLf & _
             "error file position: " & .filepos & vbCrLf & _
             "error line: " & .Line & vbCrLf & _
             "error line position: " & .linepos & vbCrLf & _
             "error reason: " & .reason & vbCrLf & _
             "error source Text: " & .srcText & vbCrLf & _
             " XSLXSLT:ApplyStyle"
     End With
  End Sub

The error handler is essentially the same code we used in the last chapter for the parserError object.

Once the source document is loaded (when its readystate is equal to 4), we will want to load the XSL or XSLT document. We will do this in the onreadystatechange event of the source document. Add the following code to the onreadystatechange event of the m_oDOMDocumentSource object:

  Private Sub m_oDOMDocumentSource_onreadystatechange()
     On Error GoTo m_oDOMDocumentSourceORSCError
     If m_oDOMDocumentSource.readyState = 4 Then
        m_oDOMDocumentStylesheet.Load (StyleSheetURL)
        If m_oDOMDocumentStylesheet.parseError.errorCode <> 0 Then
           DOMError
        End If
     End If
     Exit Sub
     m_oDOMDocumentSourceORSCError:
     Err.Raise Err.Number, Err.Source, _
        "The following error has occurred:" & vbCrLf & _
        "Error Description: " & Err.Description & vbCrLf & _
        "Error Source: " & Err.Source & _
        "XSLXSLT:m_oDOMDocumentSource:onreadystatechange" & _
        vbCrLf & "Error Number: " & Err.Number
  End Sub

In this example, we will load the XSLT or XSL document when the source document is loaded.

Because we have now begun the load for the m_oDOMDocumentStylesheet object, the next four steps in the program will be raising the onreadystatechange event of the m_oDOMDocumentStylesheet object. Once the stylesheet document has been loaded, we can apply it to the source object using the transformNodeToObject method of the IXMLDOMDocument interface. As we've learned in Chapter 11, the transformNodeToObject method will take a document object as its first parameter that contains either an XSL or XSLT document. The second parameter of the transformNodeToObject method will be an object that the transformed document will be placed in. Thus, the transformNodeToObject method will work perfectly to transform our XML documents using either XSL or XSLT. Add the following code to the onreadystatechange event of the m_oDOMDocumentStylesheet object:

  Private Sub m_oDOMDocumentStylesheet_onreadystatechange()
     On Error GoTo m_oDOMDocumentStylesheetORSCError
     If m_oDOMDocumentStylesheet.readyState = 4 Then
        m_oDOMDocumentSource.transformNodeToObject _
        m_oDOMDocumentStylesheet, m_oDOMDocumentResult
        If m_oDOMDocumentSource.parseError.errorCode <> 0 Then
           DOMError
        Else
        ReturnHTML = m_oDOMDocumentResult.xml
        End If
     End If
     Exit Sub
     m_oDOMDocumentStylesheetORSCError:
        Err.Raise Err.Number, Err.Source, _
           "The following error has occurred:" & vbCrLf & _
           "Error Description: " & Err.Description & vbCrLf & _
           "Error Source: " & Err.Source & _
           "XSLXSLT:m_oDOMDocumentStylesheet:onreadystatechange" & _
           vbCrLf & "Error Number: " & Err.Number
  End Sub

When this event is called for the fourth time-that is, when the m_oDOMDocumentStylesheet object is fully loaded, the program will continue execution in the ApplyXSLStyle method after the line of code that called the m_oDOMDocumentSource object load method, which is ApplyXSLStyle = ReturnHTML.

You will need to compile this DLL and run it under an MTS package in Microsoft Windows NT 4 or configure it as a COM+ application in Windows 2000, as shown in Figure 12-6.

Figure 12-6. The component registered under COM+ Component Services.

In this case, the COM+ application XMLTools was called. Once you have registered the component under MTS or COM+ component services, you can create an ASP page named NorthwindPO.asp that can use this object as follows:

  <%@ Language=VBScript %>
  <SCRIPT LANGUAGE=vbscript RUNAT=Server>
     Dim objXSLXSLT, sreturn
     Set objXSLXSLT = server.CreateObject ("TransformXML.Transform")
     sreturn = objXSLXSLT.ApplyXSLStyle _
        ("http://localhost/Chapter12/NorthwindPOXSLT.xml", _
           "http://localhost/Chapter12/NorthwindPO3.xslt")
     Response.Write sreturn
  </SCRIPT>

You will need to change the parameters of the ApplyXSLStyle function so that it references the files in the correct location (in the example case, the files were located on the localhost under Chapter 12). This code will work with either an XSL or an XSLT document. The page will display the same format as before.