[thelist] xml vs. regular database

David.Cantrell at Gunter.AF.mil David.Cantrell at Gunter.AF.mil
Fri Jan 17 10:13:01 CST 2003


>what kinda of custom VBScript class?  Could you give me an example?

Sure. :)

I was going to release this on the web at some point anyway, just haven't
gotten around to it. It's something that works for me.

And I blatantly ripped off an old Microsoft article in doing it, but their's
was kludged in Javascript, I converted it to VBScript and cleaned it up and
enhanced it so it's not exactly plagiarism. ;)

This may wrap in your e-mail system -- if so I can send it directly to you
as an attachment.

***** BEGIN CLASS SNIPPET *****
#FILENAME:  class.XmlUtilsObject.asp

<%
'===========================================================================
==========================================
' XmlUtilsObject
' Provides enhanced Xml capabilities, such as simplified transformation,
Recordset-to-Xml capabilities, etc.
' Base methods were taken from MSDN "Beyond ASP" article.
' AddXslParameter function added later.
'===========================================================================
==========================================
Class XmlUtilsObject



'===========================================================================
==========================================
' Public Methods
'===========================================================================
==========================================

'---------------------------------------------------------------------------
------------------------------------------
'Title:   AddXmlNode
'About:   Adds a new DomNode to the XmlDomDocument object passed in, as a
child to the parent node specified.
'Accepts: XmlDomDocument. Msxml.XmlDomDocument. Required. XML document to
which the node will be added.
'Accepts: ParentNode. DomNode. Required. The XML Node that will be the
parent of the added node.
'Accepts: Name. String. Required. The name of the node to create.
'Accepts: Value. String. Required. The value of the node that is created.
'Returns: XmlDomNode object.
Public Function AddXmlNode( oXmlDomDocument, oParentNode, sName, sValue )
  Set AddXmlNode = AddXmlNodeEx( oXmlDomDocument, oParentNode, sName,
sValue, "" )
End Function

'---------------------------------------------------------------------------
------------------------------------------
'Title:   AddXmlNodeEx
'About:   Adds a new DomNode to the XmlDomDocument passed in, as a child to
the parent node specified.
'Remarks: AddXmlNodeEx allows the caller to specify a custom namespace for
the created node.
'Accepts: XmlDomDocument. Type=Msxml.XmlDomDocument. Required. XML document
to which the node will be added.
'Accepts: ParentNode. Type=DomNode. Required. The XML Node that will be the
parent of the added node.
'Accepts: Name. String. Required. The name of the node to create.
'Accepts: Value. String. Required. The value of the node that is created.
'Accepts: Namespace. String. Optional. The namespace of the string that will
be created.
'Returns: XmlDomNode object.
'Effects: Namespace is returned as attribute value, e.g. <name
xmlns="namespace">value</name>.
Public Function AddXmlNodeEx( oXmlDomDocument, oParentNode, sName, sValue,
sNamespace )
  dim oNode : Set oNode = oXmlDomDocument.CreateNode( 1, Lcase( sName ),
sNamespace )
  If Not Len( sValue ) = 0 then
    oNode.Text = Trim( sValue )
  End If

  oParentNode.AppendChild oNode

  Set AddXmlNodeEx = oNode
  If Len( sNamespace ) > 0 then
  End If
End Function

'---------------------------------------------------------------------------
------------------------------------------
'Title:   RecordsetToXml
'About:   Processes a flat (non-hierarchical) recordset and returns a
populated XmlDomDocument object.
'Remarks: The document is in this format:
'           <pre>NodeNames
'                 NodeName
'                   Value
'                   Value
'                   ...</pre>
'         Where NodeNames is the name passed in with an "s" added, and
NodeName is simply the name passed in.
'Accepts: Recordset. Adodb.Recordset. Required. Recordset containing the
data to convert.
'Accepts: NodeName. String. Required. NodeName used in description above.
'Returns: XmlDomDocument object.
'Assumes: Recordset cursor is not set to EOF.
Public Function RecordsetToXml( oRs, sNodeName )
  dim oXml      : Set oXml      = Server.CreateObject(
"Msxml2.DomDocument.4.0" )
  dim oNodeCol  : Set oNodeCol  = AddXmlNode( oXml, oXml, sNodeName & "s",
"" )

  dim oNode
  dim oField

  Do until oRs.Eof
    Set oNode = AddXmlNode( oXml, oNodeCol, sNodeName, "" )
    For Each oField in oRs.Fields
      AddXmlNode oXml, oNode, oField.Name, Trim( oField.Value )
    Next
    oRs.MoveNext
  Loop

  Set RecordsetToXml = oXml
End Function

'---------------------------------------------------------------------------
------------------------------------------
'Title:   GetXmlDoc
'About:   Retrieves the specified Xml document as a DomDocument object.
'Remarks: The document may be either a well-formed fragment, a full Xml
document, or a relative path to a file.
'Accepts: XmlDoc. Variant. Required. String containing XSL or relative path
to XSL file, or XmlDomDocument object.
'Returns: XmlDomDocument object.
Public Function GetXmlDoc( vXmlDoc )
  Set GetXmlDoc = GetXmlDocEx( vXmlDoc, "Normal" )
End Function

'---------------------------------------------------------------------------
------------------------------------------
'Title:   GetFreeThreadedXmlDoc
'About:   Retrieves the specified Xml document as a FreeThreadedDomDocument
object.
'Remarks: The document may be either a well-formed fragment, a full Xml
document, or a relative path to a file.
'Accepts: XmlDoc. Variant. Required. String containing XSL or relative path
to XSL file, or XmlDomDocument object.
'Returns: XmlDomDocument object.
Function GetFreeThreadedXmlDoc( vXmlDoc )
  Set GetFreeThreadedXmlDoc = GetXmlDocEx( vXmlDoc, "FreeThreaded" )
End Function

'---------------------------------------------------------------------------
------------------------------------------
'Title:   AddXslParameter
'About:   Adds parameters for the stylesheet.
'Remarks: Parameters are added to an internal list and set in XSL when
document is transformed -- XSL is NOT edited interactively.
'Remarks: Bear in mind that, as you are dealing with an XML document, you
may need to escape the values you pass in the parameters.
'Accepts: ParameterName. String. Required. Name of the parameter to set.
'Accepts: ParameterValue. String. Required. Value of the parameter being
set.
'Returns: Nothing.
'Assumes: Nothing.
'Ensures: Internal list is updated with new name & value.
'Effects: If the specified parameter already exists in the list, the
original value is overwritten.
Sub AddXslParameter( sParameterName, sParameterValue )
  If Not IsObject( oXSL_PARAMETERS ) then
    Set oXSL_PARAMETERS = Server.CreateObject( "Scripting.Dictionary" )
  End If
  oXSL_PARAMETERS.Add sParameterName, sParameterValue
End Sub

'---------------------------------------------------------------------------
------------------------------------------
'Title:   TransformXml
'About:   Transforms the specified xml document using the specified xsl
document.
'Remarks: Xml and Xsl documents can be well-formed fragments, entire
documents, XmlDomDocument objects, or relative paths to files.
'Accepts: SourceXml. Variant. Required. String containing XML or relative
path to XML file, or XmlDomDocument object.
'Accepts: SourceXsl. Variant. Required. String containing XSL or relative
path to XSL file, or XmlDomDocument object.
'Returns: String containing the transformed output.
Public Function TransformXml( vSourceXml, vSourceXsl )
  dim sReturn           'the return value
  dim oXml              'the xml object
  dim oXsl              'the xsl object
  dim bHasParameters    'switch to determine processing path

  'loads XML object and checks for errors
  Set oXml = GetXmlDoc( vSourceXml )
  CheckForParseError oXml

  Validate oXml

  'determines if parameters were passed before this method was called
  If IsObject( oXSL_PARAMETERS ) then
    'ubound will return -1 if no keys (parameter names ) exist
    If Ubound( oXSL_PARAMETERS.Keys() ) >= 0 then
      bHasParameters = true
    Else
      bHasParameters = false
    End If
  Else
    bHasParameters = false
  End If

  'if parameters were specified, passes them to the stylesheet
  'otherwise simply transforms the document using the provided stylesheet
  If bHasParameters then
    Set oXsl = GetFreeThreadedXmlDoc( vSourceXsl )

    'parameters exist, creates XSL template and processor, adds parameters
to stylesheet, and transforms using processor
    dim oXslt : Set oXslt = Server.CreateObject( "Msxml2.XslTemplate.4.0" )
    Set oXslt.Stylesheet = oXsl
    dim oProc : Set oProc = oXslt.CreateProcessor()
    oProc.Input = oXml

    'loops through parameters in dictionary and adds them to the stylesheet
    dim aKey  : aKey  = oXSL_PARAMETERS.Keys()
    dim aItem : aItem = oXSL_PARAMETERS.Items()
    dim i
    For i = 0 to Ubound( aKey )
      oProc.AddParameter aKey( i ), aItem( i )
    Next

    oProc.Transform
    sReturn = oProc.Output

    Set oProc = Nothing
    Set oXslt = Nothing
  Else
    Set oXsl  = GetXmlDoc( vSourceXsl )
    sReturn   = oXml.TransformNode( oXsl )
  End If

  TransformXml = sReturn

End Function



'===========================================================================
==========================================
' Initialization and Termination Routines
'===========================================================================
==========================================
Private Sub Class_Initialize
End Sub

'---------------------------------------------------------------------------
------------------------------------------
Private Sub Class_Terminate
  'if XSL parameter dictionary exists, it is destroyed
  If IsObject( oXSL_PARAMETERS ) then
    Set oXSL_PARAMETERS = Nothing
  End If
End Sub



'===========================================================================
==========================================
' Private Properties
'===========================================================================
==========================================
Private oXSL_PARAMETERS     'Scripting.Dictionary object containing XSL
parameters



'===========================================================================
==========================================
' Private Methods
'===========================================================================
==========================================
'Title:   GetXmlDocEx
'About:   Retrieves the specified Xml document as either a DomDocument or
FreeThreadedDomDocument object.
'Remarks: The document may be either a well-formed fragment, a full Xml
document, or a relative path to a file.
'Accepts: XmlDoc. Variant. Required. String containing XSL or relative path
to XSL file, or XmlDomDocument object.
'Accepts: Type. String. Required. Type of document to return. Possible
values are "FreeThreaded" and "Normal". Default is "Normal".
'Returns: XmlDomDocument object.
Private Function GetXmlDocEx( vXmlDoc, sType )
  dim oXml

  If IsObject( vXmlDoc ) then
    'uses the object passed in
    Set oXml = vXmlDoc

  Else
    If Lcase( Trim( sType ) ) = "freethreaded" then
      'creates a FreeThreadedDomDocument object
      Set oXml = Server.CreateObject( "Msxml2.FreeThreadedDomDocument.4.0" )
    Else
      'type is "Normal", creates a normal DomDocument object
      Set oXml = Server.CreateObject( "MsXml2.DomDocument.4.0" )
    End If

    If InStr( vXmlDoc, "<" ) > 0 then
      'this is an Xml string because < is not valid in a filename
      oXml.LoadXml vXmlDoc
    ElseIf InStr( vXmlDoc, "\" ) > 0 then   '"' comment to fix stupid
homesite color bug
      'this is a physical path because \ only occurs in physical paths
      oXml.Load vXmlDoc
    Else
      oXml.Load Server.MapPath( vXmlDoc )
    End If
  End If

  Set GetXmlDocEx = oXml
End Function

'---------------------------------------------------------------------------
------------------------------------------
'Title:   CheckForParseError
'About:   Checks the passed XML object for any parsing errors. If any exist,
they are reported using ReportParseError.
'Accepts: Xml. MsxmlDomDocument. Required. The XML object to check for a
parsing error.
'Returns: Nothing.
'Assumes: Nothing.
'Ensures: Error is raised if encountered.
Private Sub CheckForParseError( oXml )
    'check for parse errors
    If Not oXml.ParseError = 0 then
      response.write ReportParseError( oXml.ParseError )
    End If
End Sub

'---------------------------------------------------------------------------
------------------------------------------
'Title:   Validate
'About:   Validates the passed XML object. If invalid, ReportParseError is
called.
'Accepts: oXml. MsxmlDomDocument. Required. The XML object to check for
validity.
'Returns: Nothing.
Private Sub Validate( oXml )
  dim oErr : Set oErr = oXml.Validate()
  If Not oErr Is Nothing then
    ReportParseError oErr
  End If
End Sub

'---------------------------------------------------------------------------
------------------------------------------
'Title:   ReportParseError
'About:   Reports a parse error.
'Accepts: Error. XmlDomDocument.ParseError. Required. Object containing XML
ParseError information.
'Returns: String containing formatted parse error.
Private Function ReportParseError( oError )
  dim s
  dim i
  For i = 1 to oError.LinePos
    s = s & " "
    i = i + 1
  Next

  dim sResult
  sResult = "<h1>XML Error loading '" & oError.Url & "'</h1>"
  sResult = sResult & "<div style='font-family: Verdana; font-size: 10pt;'>"
  sResult = sResult & "<b>Error Code:</b> " & oError.ErrorCode
  sResult = sResult & "<br><b>Reason:</b> " & oError.Reason
  sResult = sResult & "<br><b>Source:</b> Line " & oError.Line & ",
Character " & oError.LinePos
  sResult = sResult & "<br>" & oError.SrcText
  sResult = sResult & "</div>"

  ReportParseError = sResult
End Function



End Class
%>

***** END CLASS SNIPPET *****

Here's an example ASP script where it might be used:

***** BEGIN ASP SNIPPET *****

<%
Option Explicit
%>
<!--#include virtual="/path/to/class.XmlUtilsObject.asp"-->
<%
Server.Execute ( "header.asp" )

dim oConn : Set oConn = Server.CreateObject ( "connectionString" )
dim oRs : Set oRs = oConn.Execute ( "select * from employees" )

dim XmlUtils : Set XmlUtils = New XmlUtilsObject
Response.Write XmlUtils.TransformXml ( XmlUtils.RecordsetToXml ( oRs,
"employee" ), "employees.xsl" )

oRs.Close
Set oRs = Nothing
oConn.Close
Set oConn = Nothing

Server.Execute ( "footer.asp" )
%>


***** END ASP SNIPPET *****

The above code selects all records from the "employees" table and then
passes that recordset through the XmlUtils object. The
XmlUtils.RecordsetToXml() function takes the recordset (oRs) and a string
called "sNodeName" in the function definition in the class. It then builds
the XML document in the following format:

	<employees>
		<employee>
			<id>1</id>
			<name>John Doe</name>
			<deptid>45</deptid>
		</employee>
		<employee>
			...
		</employee>
	</employees>

It takes the sNodeName of "employee" passed in the function and creates the
XML document with a root node of sNodeName+s, then each row is created as a
child element with a name of sNodeName, i.e. given an sNodeName of
"employee" the root element is "employees" and each row returned is captured
in an element named "employee". Each column in the recordset is then created
as an element underneath the appropriate row element (the "employee"
element).

Also, all elements are created in lower case format, to minimize typos.

Then you just pass that through a stylesheet, like this:

***** BEGIN XSL SNIPPET *****
#FILENAME: employees.xsl

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">

	<xsl:output method="html"/>

	<xsl:template match="/">
		<h1>Employee Listing</h1>
		<ul>
			<xsl:apply-templates select="/employees/employee"/>
		</ul>
	</xsl:template>

	<xsl:template match="employee">
		<li>
			<p>
				ID: <xsl:value-of select="id"/>
				<br/>
				Name: <xsl:value-of select="name"/>
				<br/>
				Dept Id: <xsl:value-of select="deptid"/>
			</p>
		</li>
	</xsl:template>

</xsl:stylesheet>

***** END XSL SNIPPET *****


Does any of this make sense? :)

Now to answer your other question(s):

>I thought it went like this
>XML - your data file
>XSL - your language file (this is where you stick your html that goes
around
>the data in the XML file - right?)
>XSD - your definition file for your xml file...

The XML doesn't have to reside in a file -- it's just a document, just like
HTML or any other. It's created in memory, passed through a stylesheet, and
the output is written to the browser. The XSLT is Extensible Stylesheets
Language for Transformations -- yes, it's where you convert it to HTML. Or
PDF, or plain-text, or any other text format. It's just a templating system.

As for XSD, I don't really use it. I messed with it for a while, but for
what I do (which is NOT large-scale development) I don't have a need for it.
The complexity of XSD for me outweighs the benefits.

HTH,
-dave



More information about the thelist mailing list