Although Internet Explorer 5 cannot identify the namespace in an XSLT document, versions 2.6 and higher of the XML parser can use XSLT documents to transform XML documents. We will use the same BizTalk document we used earlier in this chapter, with some minor changes that will fix errors in the document, to perform the transformation using XSLT documents. Even though we did not receive any error messages when we used the BizTalk document, two errors do exist in the code. The following two elements are not defined in the schema as being children of the POlines element:
<count>2</count> <totalAmount>192000.00</totalAmount> |
If you try to work with the BizTalk document using the XML DOM, the load method of the IXMLDOMDocument interface will fail. It's important to note this, as we have come this far with no hint that there was an error in the document. This stresses the importance of validating your documents. To correct the error, delete the above two elements from the XML document and rename the document NorthwindPOXSLT.xml. You can also delete the references to these two elements in your XSL and XSLT document. Let's now look at a code sample, which you could use in your production systems, that shows how to code with the XML DOM.
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.
NOTE
This Visual Basic component will need to run under Microsoft Transaction Server (MTS) to work properly with an ASP document. We won't discuss how to write MTS components in Visual Basic as that would go beyond the scope of this book.
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.