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.