The C_XML class is responsible for formatting the XML string that is returned when a calling application requests the XML data format. It has a single public method (BuildXML) that formats the XML for a given Recordset object. The C_XML class is only used if XML was requested by the calling application.
|
Property |
Setting |
Reasoning |
|
Instancing |
MultiUse |
This class will be instantiated in our DLL
running under MTS. |
|
MTSTransactionMode |
NoTransactions |
We don't want to join any MTS transactions initiated by the calling program. This object doesn't interact with an underlying data source, so there is no need to have it join a transaction. |
In the following code section, we define a constant that holds the name of our class. Next, since we want to build our code to run under MTS, we implement the ObjectControl interface and all of its methods. As in the C_Data_ADO_MTS class, this gives MTS a way to tell us when the class is activated and deactivated under MTS.
Then we create a private Type that holds all of our class level variables. This time we have two class level variables: oObjCtx and oXML. We discussed the purpose of the oObjCtx object when we examined the C_Data_ADO_MTS class. The oXML object variable, however, is new to our code. This variable references an instance of the MSXML.DOMDocument object library. We'll use the library to generate XML, based on the Recordset we pass in to this class' BuildXML method (shown in the next section).
Option Explicit
'------------------------------------------------------------------------------
'--- *** You may use this code as long as you keep these comments in place. ***
'--- ORIGINALLY WRITTEN BY: Blue Sand Software, Inc.
'--- DATE WRITTEN: December 1999
'---
'------------------------------------------------------------------------------
'--- Name of this object
Private Const msOBJ_NAME As String = "C_XML"
'--- MTS Object control interface
Implements ObjectControl
Private Type typeThis
oObjCtx As ObjectContext '--- MTS Object context
oXML As MSXML.DOMDocument '--- XML Document Object
End Type
Private m As typeThis
The BuildXML method is the only public interface for the C_XML class. It accepts an ADO Recordset object and builds an XML document using the MSXML object library. This method is quite lengthy, but I tried to comment it well enough so that you can follow it line by line.
It starts out by creating the metadata nodes in the XML stream. To create this part of the XML, we loop through the Fields collection of the Recordset and add each Field and its attributes to the XML stream:
Public Function BuildXML(ByVal oRs As Recordset) As String
'--- Build XML from the data and metadata
Const sPROC_NAME As String = msOBJ_NAME & ".BuildXML"
Const lMetaDataTag As Long = 0
Const lMetaDataFieldsTag As Long = 0
Const lDataTag As Long = 1
Const lDataRowsTag As Long = 0
Const lDataRowsRowFieldsTag As Long = 0
Dim lRow As Long
Dim lCol As Long
Dim sXML As String
If oRs.EOF And oRs.BOF Then Exit Function
'--- Create XML document element
Set m.oXML.documentElement = m.oXML.createElement("xml")
'--- With <xml>
With m.oXML.documentElement
'-------------------------------------------------------
'--- CREATE METADATA
'-------------------------------------------------------
'--- Create MetaData node
.appendChild CreateNode("metadata")
'--- Create MetaData Fields node
.childNodes(lMetaDataTag).appendChild CreateNode("fields")
'--- With <fields>
With .childNodes(lMetaDataTag).childNodes(lMetaDataFieldsTag)
'--- Loop through Recordset fields to
'--- create MetaData Field nodes
For lCol = 0 To oRs.Fields.Count - 1
'--- Create the Field node
.appendChild CreateNode("field")
'--- Create all of the metadata attributes
'--- With <field> attributes
With .childNodes(lCol).Attributes
'--- Create Index attribute
.setNamedItem CreateAttribute("Index", lCol)
'--- Create Name attribute
.setNamedItem CreateAttribute("Name", oRs.Fields(lCol).Name)
'--- Create Type attribute
.setNamedItem CreateAttribute("Type", oRs.Fields(lCol).Type)
'--- Create DefinedSize attribute
.setNamedItem
CreateAttribute("DefinedSize", oRs.Fields(lCol).DefinedSize)
'--- Create NumericScale attribute
.setNamedItem
CreateAttribute("NumericScale", oRs.Fields(lCol).NumericScale)
'--- Create Precision attribute
.setNamedItem CreateAttribute("Precision", oRs.Fields(lCol).Precision)
'--- Create IsIdentity attribute
.setNamedItem CreateAttribute("IsIdentity", _
IIf(oRs.Fields(lCol).Properties.Item("ISAUTOINCREMENT"),
_
"True", "False"))
'--- Create IsNullable attribute
.setNamedItem CreateAttribute("IsNullable", _
IIf(CBool(oRs.Fields(lCol).Attributes And _
adFldIsNullable), "True", "False"))
'--- Create Updateable attribute
.setNamedItem CreateAttribute("Updateable", _
IIf(CBool(oRs.Fields(lCol).Attributes And _
adFldUnknownUpdatable), "True",
"False"))
'--- Create FixedLength attribute
.setNamedItem CreateAttribute("FixedLength", _
IIf(CBool(oRs.Fields(lCol).Attributes And adFldFixed), _
"True", "False"))
'--- Create LongBinary attribute
.setNamedItem CreateAttribute("LongBinary", _
IIf(CBool(oRs.Fields(lCol).Attributes And adFldLong), _
"True", "False"))
'--- Create PrimaryKey attribute
.setNamedItem CreateAttribute("PrimaryKey", _
IIf(oRs.Fields(lCol).Properties.Item("KEYCOLUMN"), _
"True", "False"))
End With
Next
End With
'-------------------------------------------------------
'--- CREATE DATA
'-------------------------------------------------------
'--- Create Data node
.appendChild CreateNode("data")
'--- Create Data Rows node
.childNodes(lDataTag).appendChild CreateNode("rows")
oRs.MoveFirst
'--- With <rows>
With .childNodes(lDataTag).childNodes(lDataRowsTag)
Do While Not oRs.EOF
lRow = oRs.AbsolutePosition - 1
'--- Create the Row node
.appendChild CreateNode("row")
'--- Create Index attribute
.childNodes(lRow).Attributes.setNamedItem CreateAttribute("Index", lRow)
'--- Create the Fields node
.childNodes(lRow).appendChild CreateNode("fields")
'--- With <fields>
With .childNodes(lRow).childNodes(lDataRowsRowFieldsTag)
'--- Loop through Recordset fields to
'--- create Data Field nodes
For lCol = 0 To oRs.Fields.Count - 1
'--- Create the Field node
.appendChild CreateNode("field")
'--- With <field>
With .childNodes(lCol)
'--- Create Index attribute
.Attributes.setNamedItem CreateAttribute("Index", lCol)
'--- Create Name attribute
.Attributes.setNamedItem
CreateAttribute("Name", _
oRs.Fields(lCol).Name)
'--- Create Field Data
.Text = oRs.Fields(lCol).Value
End With
Next
End With
oRs.MoveNext
Loop
End With
End With
sXML = m.oXML.xml
Set oRs = Nothing
BuildXML = sXML
'----------------------------------------------------------------
'--- Complete the MTS Transaction
'----------------------------------------------------------------
Set m.oXML = Nothing
m.oObjCtx.SetComplete
Exit Function
Err:
'----------------------------------------------------------------
'--- Abort the MTS Transaction
'----------------------------------------------------------------
Dim sError As String: sError = Err.Description
Dim sSource As String: sSource = Err.Source
Dim lErrNum As Long: lErrNum = Err.Number
On Error Resume Next
'----------------------------------------------------------------
'--- Cleanup
'----------------------------------------------------------------
Set oRs = Nothing
'----------------------------------------------------------------
'--- Abort the transaction
'----------------------------------------------------------------
m.oObjCtx.SetAbort
On Error GoTo 0
Err.Raise lErrNum, sSource & " <--- " & sPROC_NAME, sError
End Function
Then we get to the code that generates the data nodes (CreateNode
and CreateAttribute)
in the XML stream. To create this part of the XML, we loop through the rows of
data in the Recordset.
Then, for each row node, we loop through the Fields collection of the Recordset
and add each Field
and its value to the XML stream. In the end, we have a string of XML that we
pass back to the C_Data_ADO_MTS class' ExecuteSQL method and, from there,
back to the calling application. Once there, we can use the XML to display the
data in Internet Explorer or we can pass it to another application for
processing.
This method is simply used to abstract the creation of XML element nodes (i.e. <fields> or <rows>):
Private Function CreateNode(ByVal sName As String) As MSXML.IXMLDOMNode
Set CreateNode = m.oXML.CreateNode(NODE_ELEMENT, sName, "")
End Function
This method is simply used to abstract the creation of XML element attribute nodes. In other words, we use this function to define an attribute and its value on a given XML element. For example, we could use this function to define the Index attribute of a <field> tag to result in: <field Index="7">.
Private Function CreateAttribute(ByVal sName As String, _
ByVal vValue As Variant) As MSXML.IXMLDOMNode
Dim oXMLNode As MSXML.IXMLDOMNode
Set oXMLNode = m.oXML.CreateNode(NODE_ATTRIBUTE, sName, "")
oXMLNode.nodeTypedValue = vValue
Set CreateAttribute = oXMLNode
End Function
Again, since we are integrating this class with MTS and have declared that we intend to implement the ObjectControl interface, we must implement all of the methods of the interface (shown in the following code section). The ObjectControl_Activate method is invoked by MTS when a calling application requests a C_XML object and the ObjectControl_Deactivate method is invoked by MTS when a calling application is finished with the instance of the object.
In the ObjectControl_Activate method, we also create an instance of the MSXML.DOMDocument object. We'll be using the object throughout this class as we generate the XML document, so it will be handy to keep it around during the life of this object.
Private Sub ObjectControl_Activate()
On Error GoTo 0
If m.oObjCtx Is Nothing Then
Set m.oObjCtx = GetObjectContext()
Set m.oXML = CreateObject("MSXML.DOMDocument")
End If
End Sub
Private Function ObjectControl_CanBePooled() As Boolean
ObjectControl_CanBePooled = True
End Function
Private Sub ObjectControl_Deactivate()
On Error GoTo 0
'--- Clean up
Set m.oObjCtx = Nothing
Set m.oXML = Nothing