| Programmer to ProgrammerTM | |||||
|
|||||
|
|
|
||||
|
|
|
|
|
|
|
|
|
|
| |||||||||||||||||||||
| The ASPToday
Article October 31, 2001 |
Previous
article - October 30, 2001 |
||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||
| ABSTRACT |
| ||||||||||||||||||||||||||||||
|
| |||||||||||||||||||||||||||||||
| Article Discussion | Rate this article | Related Links | Index Entries | ||||||||
| ARTICLE | |||||||||||
With so much data being represented using XML, and with so much of that XML being transferred between systems, how can we be 100% sure that the XML elements and attributes contain valid data? After all, potentially we are transferring data between different platforms/operating systems and between different applications - history has shown us that this is an area fraught with danger. The answer is to use XML Schema to describe the XML elements and attributes using data types, sequences, regular expressions and occurrences. An XML document whose elements and attributes adhere to the "rules" provided by an XML schema document is said to be "valid". However, an XML document that is "well-formed", i.e. it is legal XML, may well be "invalid" if the elements and attributes fail to "validate" against a related XML schema document.
XML Schema forms part of the standards work performed by the World Wide Web Consortium (W3C). The W3C are also responsible for bringing us the Document Object Model (DOM) recommendations for HTML and XML - so it is very likely that you will be familiar with their work. During May 2001, XML Schema became a W3C Recommendation, thus we can expect a surge of products that support the Recommendation.
This article will cover:
This article assumes that you are familiar with basic XML concepts. Some knowledge of Visual Basic 6 is required. I will be using the Microsoft XML Parser Version 4 (MSXML4) - specifically I will be using the July 2001 preview of MSXML4. If you wish to download MSXML4, there is a URL provided in the Resources section of this article. The code download that accompanies this article requires Visual Basic 6 and MSXML4
The Visual Studio .NET (VS.NET) XML Designer is discussed, albeit briefly. Such support for XML Schema is something of a prophetic vision. XML Schema is appearing in many facets of our development portfolio; for example, XML Schema can be found in SOAP, WSDL and SQL Server's XML support. With the might of Microsoft (and the W3C) behind XML Schema, gaining an understanding of it is going to be paramount. On a similar note, comparable production-class support for XML Schema can be found in Borland Delphi 6.
Whilst I have used Visual Studio .NET (VS.NET) beta 2, you do not need it in order to benefit from this article. If you are reading this article with a release version of VS.NET, you should expect to see minor differences between this article and the release version.
In a typical client/server scenario the only way we could be reasonably sure that the data we are about to transfer from the client to the server is to perform some clever client-side data validation. This works if all your clients are on the same platform, perhaps inside a JavaScript-aware browser, or as part of a custom application. If your clients use multiple platforms, you will find yourself writing and maintaining the same clever data validation code for however many platforms you need to support - a very daunting task.
Moving the data validation from the client to the server is also fraught with danger. Scalability is impaired if the server has to perform any more work that is really necessary. Whilst the validation itself might not be very processor intensive, maintaining state becomes a real issue if the validation fails. After all, if validation fails, the server must advise the client and must do so in a user friendly fashion - if the user is filling in an on-line form they will not appreciate having to complete the entire form in again if they failed to fill in one field!
Early mechanisms for describing an XML document to the extent of being able to "validate", required the use of a Document Type Definition (DTD). However DTDs do have their failing. DTDs do not use XML syntax - this makes them difficult to parse. DTDs do not allow element-level data typing, thus the amount of validation a DTD can achieve is somewhat limited. After all, we are all keen to validate dates and strings, etc. so we need a mechanism that is capable of allowing the use of common data types. Similarly, with the increased importance of XML namespaces, the namespace support offered by DTDs will soon prove exceptionally inflexible. XML Schema addresses all of these shortcomings and brings with it a richer environment for data description.
XML Schema allows us to define the structure of an XML document - put simply, we can specify the order in which elements and attributes should appear. In addition, we can provide some 'type information', e.g. we are able to specify that a <count> element may only contain a value of type integer. Essentially, XML Schema is about representing the "data about data", i.e. the Meta-data.
The beauty of XML Schema is that, unlike DTDs, it uses XML syntax to describe XML. This has the added benefit that existing XML parsers, such as the Microsoft XML Parser (MSXML4), are able to parse XML schema documents. We will look at MSXML4 in more detail later in this article. The MSXML4 (July 2001 web release) provides support for the W3C XML Schema Proposed Recommendation. The XML Schema presented in this article and the explanations provided, are based on the W3C XML Schema Recommendation. For the purposes of this article, the differences between the Proposed Recommendation and the Recommendation itself, do not pose to be a problem.
Rather than attempt to describe XML Schema, I will walk through an example. The example files are available as part of the download associated with this article - they are in the Ex1 directory.
Consider the following XML document (request.xml):
<?xml version="1.0"?>
<r:request xmlns:r="urn:request">
<contact>
<company>Company</company>
</contact>
<address>
<line1>A House</line1>
<line2>1 Street</line2>
<line3>Suburb</line3>
<line4>City</line4>
<postcode>B11 611</postcode>
</address>
</r:request>
Before I go on to explain this XML Schema, it is worth noting that the <request> element is scoped by a namespace - "urn:request ". We need to provide a namespace because the XML Schema will refer to it. A full discussion about XML namespaces is beyond the scope of this article, however there is a great tutorial about XML namespaces here: http://www.devxpert.com/tutors/xmlns/xmlns.asp.
A simple XML Schema ( simple_request.xsd) for request.xml might be:
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="urn:request">
<xsd:element name="request">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="contact">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="ref_no"
type="xsd:integer"
minOccurs="0"
maxOccurs="1"/>
<xsd:element name="company" type="xsd:string" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="address">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="line1" type="xsd:string" />
<xsd:element name="line2" type="xsd:string" />
<xsd:element name="line3" type="xsd:string" />
<xsd:element name="line4" type="xsd:string" />
<xsd:element name="postcode" type="xsd:string" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
As I mentioned earlier an XML Schema is nothing more than an XML document, hence it has the usual processing instruction:
<?xml version="1.0" encoding="UTF-8"?>
We then define the root element <schema>:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="urn:request">
I have chosen to use the namespace prefix xsd. This has the side effect of making the XML Schema elements/attribute easily identifiable. The target Namespace attribute associates this XML Schema with the XML data that is scoped by "urn:request " - i.e. this XML Schema is targeted against the namespace that we used in request.xml.
Our XML document, request.xml, has a root element <request>. XML Schema allows us to define XML elements using the <element> element. In the case of <request>, the <element> takes one attribute name:
<xsd:element name="request">
Inside the <request> element, we nest <contact> and <address> elements - XML Schema defines "an element that contains other elements" as a complexType. As we will see later in this article, complexTypes can be used to define/construct our own 'custom' types.
Where elements are nested, and they are to appear in a certain order, XML Schema offers us the notion of composition via the use of a <xsd:sequence> element. Using the <xsd:sequence> element may prove useful if you are accessing XML elements via the XML Document Object Model (DOM) methods firstChild, lastChild, nextSibling, previousSibling or via the childNodes collection. If you use these methods to traverse your XML document, generally speaking, you are assuming that elements appear in a certain order. Validating their order prior to traversal will determine whether the elements are in the order you are expecting them to be in.
Thus the XML Schema for the <contact> element so far is:
<xsd:complexType>
<xsd:sequence>
<xsd:element name="contact">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="ref_no"
type="xsd:integer"
minOccurs="0"
maxOccurs="1"/>
<xsd:element name="company" type="xsd:string" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
...
</xsd:sequence>
</xsd:complexType>
We can see that the <contact> element itself possibly contains nested elements, <ref_no> and <company>. Elements have a data type, such as a string, an integer, a date, etc. XML Schema allows us to represent data types using the type attribute.
XML Schema allows us to indicate elements/attributes that are considered optional. For example, we are able to specify that the element <ref_no> is optional (i.e. the minimum number of occurrences is zero). We are also able to specify that if the <ref_no> element does appear, that it may only occur once and once only. The attributes minOccurs and maxOccurs indicate the minimum and maximum number of occurrence that an element/attribute may have - XML Schema refers to this as "element/attribute cardinality". The following snippet of XML Schema provides the schema for the <ref_no> element:
<xsd:element name="ref_no"
type="xsd:integer"
minOccurs="0"
maxOccurs="1"/>
<xsd:element name="company" type="xsd:string" />
So, <ref_no> is optional ( minOccurs="0"), if it does appear, it may only appear once (before the <company> element) and it is of type integer.
XML Schema is more powerful than we have just seen. We are able to split our data into very granular pieces by defining our own data types. This will prove an invaluable means of modeling business structures without being limited to a finite set of [primitive] data types.
Consider Figure 1, below, it presents the logical hierarchy of data types for request.xml:

Figure 1 - simple data type hierarchy
Being able to define new types allows us to create the following XML Schema ( complex_schema.xsd):
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="urn:request"
xmlns:r="urn:request">
<xsd:element name="request" type="r:requestType" />
<xsd:complexType name="requestType">
<xsd:sequence>
<xsd:element name="contact" type="r:contactType" />
<xsd:element name="address" type="r:addressType" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="contactType">
<xsd:sequence>
<xsd:element name="ref_no" type="xsd:integer" minOccurs="0" maxOccurs="1"/>
<xsd:element name="company" type="xsd:string" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="addressType">
<xsd:sequence>
<xsd:element name="line1" type="xsd:string" />
<xsd:element name="line2" type="xsd:string" />
<xsd:element name="line3" type="xsd:string" />
<xsd:element name="line4" type="xsd:string" />
<xsd:element name="postcode" type="r:postcodeType" />
</xsd:sequence>
</xsd:complexType>
<xsd:simpleType name="postcodeType">
<xsd:restriction base="xsd:string">
<xsd:length value="7" fixed="true"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:schema>
XML Schema allows us to model new types, such as postcodeType, using existing XML Schema types. The idea of inheritance is a common feature and one we would expect to find in a modeling language such as XML Schema. As you might imagine, the idea of creating 'a schema' from a database is not new. However the idea of creating a schema from your business objects is perhaps a little surprising.
Our postcodeType inherits all the features of the XML Schema string type, however in order to limit the length of the string, we use the notion of an XML Schema restriction. Restrictive-inheritance is achieved via the <xsd:restriction> element - it has an attribute base that specifies the parent data type, in this case xsd:string.
To complete the postcodeType definition we must provide some information that defines the length of the string that makes up a postcode. The <xsd:length> element is known as a data type facet - it takes two attributes: value and fixed. The value attribute specifies the maximum length of the string. The fixed attribute determines whether the string must be 7 characters in length or whether may be a string of less than 7 characters. In this case, a UK postcode is 7 characters in length, so we set the fixed attribute equal to true.
Data type facets are a major plus point. In addition to the length facet, XML Schema offers a number of other facets such as maxLength, minLength and pattern. The pattern facet is extremely powerful - it allows us to provide a regular expression that can be used to validate the data an element may contain. For example, if we took our postcodeType to the extreme, we could define postcodeType like this:
<xsd:simpleType name="postcodeType">
<xsd:restriction base="xsd:string">
<xsd:pattern value="[A-Z]{2}\d\s\d[A-Z]{2}" />
</xsd:restriction>
</xsd:simpleType>
For the sake of this article, however, the postcodeType is defined like this:
<xsd:simpleType name="postcodeType">
<xsd:restriction base="xsd:string">
<xsd:length value="7" fixed="true" />
</xsd:restriction>
</xsd:simpleType>
Now that we have taken a look at the basics of XML Schema, let's move on to look at how we can actually use an XML Schema in an application.
An XML Schema is only useful if we are able to use it to validate an existing XML document, and we need to be able to perform this validation programmatically - we will develop a small Visual Basic application to perform this validation. Those of you following the development of the Microsoft Parser, MSXML, will know that Microsoft favor 'web releases' to get new functionality out to the general public for testing. At the time of writing, the Microsoft XML 4.0 Technology Preview - July 2001 was available. This release offers considerably more support for XML Schema than the previous release (April 2001) - notably, it supports the W3C 16 March 2001 XML Schema Proposed Recommendation. So, whilst it does not support the XML Schema Recommendation (2 May 2001) itself, you can be sure the next web release probably will.
Assuming that you have downloaded and installed MSXML4, the first thing you will need to do is add a reference to MSXML4. Figure 2, below, demonstrates how we can add a reference to MSXML4 using Visual Basic 6. The code/Visual Basic project files can be found in this article's code download - in the Ex1 directory.

Figure 2 - Adding MSXML4 to your project
I have created a small application that allows us to use MSXML4 to validate XML documents against their XML Schemas. To achieve this, MSXML4 provides an XMLSchemaCache object. As you might expect from a 'cache', this object allows us to load more than one schema at a time. This gives us the immediate benefit of being able to use more than one XML Schema to validate a single XML document. Multiple schemas might sound like a scenario that brings on headaches; however, think about one of the reasons why we use XML - aggregation. If an XML document contains data from different sources (such as internal departments), surely it makes sense to maintain separate schemas for each of those departments? This is in contrast to the DTD approach - an XML document can be validated against a single DTD. As a result the DTD becomes large and unwieldy.
The following code snippet presents a VB function that takes two parameters: the filename of an XML document and the filename of the associated XML Schema:
Private Sub LoadAndValidate(sXMLFileName, sXMLSchemaFileName As String)
Dim xmlRequest As DOMDocument40
Dim xmlSchema As XMLSchemaCache40
Set xmlRequest = New DOMDocument40
Set xmlSchema = New XMLSchemaCache40
' Load the schema
xmlSchema.Add "urn:request", sXMLSchemaFileName
xmlRequest.async = False
' Assign the simple_request schema to the xmlRequest DOM
' Whenever a document is loaded in to the xmlRequest DOM,
' it will be validated using the simple_request schema.
Set xmlRequest.schemas = xmlSchema
' Load & Validate against simple_request.xsd
xmlRequest.Load (sXMLFileName)
txtXML.Text = ""
lblError.Visible = False
' Was there an error?
If xmlRequest.parseError.errorCode <> 0 Then
txtXML.Text = xmlRequest.parseError.reason
lblError.Visible = True
Else
txtXML.Text = xmlRequest.xml
End If
Set xmlRequest = Nothing
Set xmlSchema = Nothing
End Sub
The logic flow through LoadAndValidate is fairly straightforward. Firstly we create instances of a DOMDocument40 and XMLSchemaCache40. It is important to emphasize that the '40' is important - XML Schema support is limited in the earlier versions of MSXML. Then we load the XML Schema specified (in our case it will be simple_request.xsd): xmlSchema.Add "urn:request", sXMLSchemaFileName. The XMLSchemaCache object exposes the Add method - it takes two parameters: a namespace and a filename. The Add method allows us to combine schemas, which means a single XML document can be validated against more than one schema. This has the clear benefit that schema maintenance is simplified. Schemas can be organized departmentally, i.e. closer to the schema 'owner'. Contrast this with the notion of organizing schemas centrally - as it was with DTDs.
I have created two XML documents, request.xml and bad_request.xml - both can be found in the Ex1 directory of this article's code download. Clicking on the Load & Validate ( request.xml) button puts the LoadAndValidate method to work. The XML document request.xml will validate without any errors - Figure 3 presents a screenshot

Figure 3 - If everything is valid then we receive no validation errors
However, there comes a time when things do not go as easily as this.
To see XML Schema validation at work, we need some "bad" XML, i.e. some XML that we know will not validate correctly. I have modified request.xml; specifically I have removed the <postcode> element. The file is called bad_request.xml:
<?xml version="1.0"?>
<r:request xmlns:r="urn:request">
<contact>
<company>Sample company.</company>
</contact>
<address>
<line1>A house</line1>
<line2>1 Street</line2>
<line3>Suburb</line3>
<line4>City</line4>
</address>
</r:request>
Clicking on Load & Validate ( bad_request.xml) presents a different picture. Figure 4, below, demonstrates that MSXML4 has successfully loaded our XML document, bad_request.xml, and has validated it. Clearly the validation has worked - the <postcode> element is indeed missing.

Figure 4 - However, if some is wrong, we get a human readable error message
Now that we have looked at the basics of XML Schema, and have proven that XML documents can be validated against an XML Schema, let's take a brief look at the XML Designer tool that is in Visual Studio .NET.
Building XML Schemas manually is a tedious and laborious task. A better approach would be to use a graphical modeling tool. Given that we are all interested in where Microsoft is going with their .NET platform, a brief discussion of the XML Designer found in VS.NET seems relevant. As I mentioned earlier, if Microsoft is putting considerable effort into supporting XML and XML Schema in .NET, then it must be worth looking at. After all, Microsoft was an early adopter/promoter of XML and SOAP, so there should be some mileage in XML Schema.
Figure 5, below, presents complex_request.xsd after it has been loaded into VS.NET - I have annotated the diagram to aid explanation.

Figure 5 - complex_request.xsd modeled using the VS.NET XML Designer
Let's go through figure 5:
Graphically modeling structures is preferable to building text files by hand - such an approach is prone to error. Figure 6, below, presents VS.NET analyzing our postcodeType. As you can see, the use of the property editor allows us to build our XML Schema using drop-down menus to select the common XML Schema facets.

Figure 6 - graphically modeling the simpleType postcodeType
Using a modeling tool such as XML Designer brings with it the obvious benefit of 'schema verification'. Modeling tools rarely let you create a diagram that is incorrect, thus you can be sure that the XML Schema generated by XML Designer is in fact valid - this is a good thing, as creating XML Schemas manually can easily lead to the creation of schemas that do not follow the W3C Recommendation. However, if you are not using a modeling tool, then there are a few schema verification tools available - I will now discuss one such tool: the IBM Schema Quality Checker.
So far we have seen that an XML schema can be used to validate XML data, however how can we be sure that the XML schema is itself correct? A "Schema Verification" tool, such as the IBM Schema Quality Checker (SQC), allows us to verify that our XML schema meets the W3C Recommendation.
Schema verification can be an extra time-consuming step. Depending upon your architecture, it may be possible to use schema verification tools during the build and test phases of your project. This is especially true if you are able to use statically built XML schemas - when you are sure that your application is ready to deploy schema verification can be disabled. However, if your XML schema is built dynamically, you will need to be sure that your schema generation code is correct - and that may involve using schema verification tools after deployment. Of course if your schema generation code creates only a finite subset of the XML Schema Recommendation, then you will be able to disable schema verification prior to deployment of your system.
The SQC tool is command-line driven and requires a Java Runtime Environment (JRE) version 1.3. To convince us that the SQC tool does what it says it does, I created bad_request.xsd. There is one known error in this schema:
<xsd:element name="company" type="xsd:STRING" />
XML Schema data types are case sensitive; hence 'STRING' is not the same as 'string'. Assuming that bad_request.xsd is in the same directory as the SQC tool, verifying bad_request.xsd is the simple matter of running it through the SQC tool using the command-line:
sqc bad_request.xsd
Figure 7, below, presents the expected output from the SQC tool. Notice how the invalid STRING is identified as an error.
Figure 7 - bad_schema.xsd fails to verify using the Schema Quality Checker
With more and more XML Schemas being defined, schema verification tools are going to prove an invaluable addition to our development portfolios. Ultimately, however, schema verification is built in to the development tools we are already using or are going to be using, such as VS.NET and XML Spy.
So far we have examined XML Schema, using XML Schema to validate an XML document and verifying the correctness of an XML Schema document. We will now take a look at how we can use this knowledge to extract XML Schemas from our existing ADO recordsets. Before we do that, however, we should consider how we could create XML from an ADO recordset - after all, once we have figured out how we can achieve that, generating the XML Schema is the next step.
With so much data being held in databases accessible through ADO, it seems logical that we will find ourselves converting data into XML for transmission to a client application or to a web page. As I mentioned earlier, creating XML Schema documents manually is tedious task. Although we cannot build an XML Schema directly from an ADO recordset, we can go some way to building a simple schema that can be used as a starting point. However before we look at creating an XML Schema from an ADO recordset, let's see how we convert an ADO recordset into XML.
I will be using Northwind.mdb for this example. The code for this example can be found in the Ex2 directory of the code download.
SELECT Shippers.ShipperID, Shippers.CompanyName, Shippers.Phone, Orders.OrderID, & - Orders.ShippedDate, Orders.Freight, Orders.ShipCity FROM Shippers INNER JOIN Orders ON Shippers.ShipperID = & - Orders.ShipVia WHERE (((Orders.ShippedDate) Is Not Null) AND ((Orders.OrderID) BETWEEN 10200 and 10300)) ORDER BY & - Shippers.ShipperID, Orders.ShippedDate;
The SQL statement above returns this recordset (formatting has been applied):
Figure 8
Figure 9 presents the XML that would be generated had we performed rs.Save "northwind.xml", adPersistXML. Whilst the XML that has been created is acceptable, and it is valid, each row has been persisted into a <z:row> element and all columns are attributes. Using adPersistXML is the easy option. The sender (the server) can essentially fire and forget - the downstream receiver (the client) application has to do a lot of work before the XML document is useable.
Assuming that we are using a modern protocol such as SOAP (which is considered element-centric) using adPersistXML to create attribute-centric XML, does present us with a problem. We need a technique that allows us to create element-centric XML from an ADO recordset. Iterating over the recordset creating XML as we go is one solution, however it is not reusable nor is it generic.
Figure 9 also demonstrates a loss of hierarchy - we are presented with a collapsed <s:Schema> element and an expanded <rs:data> element. That is all the collapsing and expanding we can achieve with this recordset. Would it not be useful to have hierarchy maintained?
Figure 9 - the
abbreviated record returned by persisting ADO to XML using adPersistXML
Using adPersistXML does generate a <Schema> element that contains a reasonable amount of information about the <data> element. However the schema that is created does not follow the XML Schema recommendation that the rest of the world is rapidly adopting - thus using adPersistXML may cause maintenance issues for you later in your project lifecycle.
A colleague of mine, David Docherty developed a technique that allows hierarchical ADO recordsets to be persisted to an XML document, whilst maintaining the hierarchy. This technique means a little extra work for the sender (the server) but offers the receiver (the client) the ability to work with master-detail recordsets in a way that adPersistXML precludes.
For the remainder of this analysis, assume that the following variables exist:
Dim vIDValue, vHash, vSQL, vDocPath As String
Dim objConn As ADODB.Connection
Dim objRst As ADODB.Recordset
Dim xmldoc As MSXML2.DOMDocument
Dim Level, vLowestLevel, lvl, idx As Integer
Set objConn = New ADODB.Connection
Set objRst = New ADODB.Recordset
Converting ADO to XML requires six steps. The first step is relatively painless; we simply define the Data Source Name (DSN). As you can see I have created a DSN called "Northwind" - it points to Northwind.mdb. Here is the code fragment that achieves this:
'------------------------------
'Customise (1 of 6). Enter DSN:
'------------------------------
objConn.Open "DSN=Northwind"
Set objRst.ActiveConnection = objConn
Step 2 involves defining the SQL that will be used to populate our ADO recordset. The following code fragment achieves this:
'------------------------------
'Customise (2 of 6). Enter SQL:
'------------------------------
vSQL = "SELECT Shippers.ShipperID, Shippers.CompanyName,"
vSQL = vSQL & " Shippers.Phone, Orders.OrderID,"
vSQL = vSQL & " Orders.ShippedDate, Orders.Freight,"
vSQL = vSQL & " Orders.ShipCity"
vSQL = vSQL & " FROM Shippers INNER JOIN Orders"
vSQL = vSQL & " ON Shippers.ShipperID = Orders.ShipVia"
vSQL = vSQL & " WHERE (((Orders.ShippedDate) Is Not Null)"
vSQL = vSQL & " AND ((Orders.OrderID) BETWEEN 10200 and 10300))"
vSQL = vSQL & " ORDER BY Shippers.ShipperID, Orders.ShippedDate;"
objRst.Open vSQL
So far, the code we have written has not been rocket science, in all honesty is fairly standard code. Step 3 is where it gets interesting - we start to define the XML elements:
'------------------------------------------------------------------
'Customize (3 of 6). Name recordset hierarchy entities (no spaces):
'------------------------------------------------------------------
Dim vEntity(2)
vEntity(0) = "ShippingCosts" '(root)
vEntity(1) = "Shipper"
vEntity(2) = "Order"
Essentially the vEntity array will be used to create the hierarchy that adPersistXML could not. Initially vEntity is nothing more than an array of strings. For the moment, think of vEntity as being the mechanism that will allow us to create this XML structure:
<ShippingCosts>
<Shipper>
<Order>
</Shipper>
.
</ShippingCosts>
Step 4:
'----------------------------------------------------------------
'Customise (4 of 6). Indicate which recordset column serves
'as unique identifier for each entity (zero-based column number).
'----------------------------------------------------------------
Dim vColUID(2)
'vColUID(0) is not required.
vColUID(1) = 0
vColUID(2) = 3
Step 5 gives us fine control over the record-level XML element/attribute creation. Recall that the SQL statement selects seven fields, thus we must specify whether each field is to be represented by an element or an attribute.
'------------------------------------------------------------------
'Customise (5 of 6). Assign recordset columns to their entity-level
'and indicate if column is an attribute (A) or a child element (E)
'(use zero-based column numbers). E.g. "1A" creates an attribute of
'the level 1 entity and "2E" creates an element which is a child
'element to the level 2 entity:
'------------------------------------------------------------------
Dim aCol(7)
aCol(0) = "1A"
aCol(1) = "1A"
aCol(2) = "1A"
aCol(3) = "2A"
aCol(4) = "2E"
aCol(5) = "2E"
aCol(6) = "2E"
Assuming that the aCol array is defined as above, this will allow us to create XML where the <Shipper> (level/depth 1) element consists of three attributes. The three attributes take their name and values from the first three fields returned by the SQL SELECT statement, i.e. ShipperID, CompanyName and Phone.
The remaining four array items, aCol(3) through to aCol(6) allow us to define the <Order> (level/depth 2) structure. <Order> will consist of an attribute matching the 4th field returned by the SQL SELECT statement, OrderID, followed by three elements: ShippedDate, Freight and ShipCity.
That took some explaining. In essence, the aCol array will allow us to create the following XML:
<Shipper ShipperID="1"
CompanyName="Speedy Express"
Phone="(503) 555-9831">
<Order OrderID="10249">
<ShippedDate>10/07/1996</ShippedDate>
<Freight>11.61</Freight>
<ShipCity>Münster</ShipCity>
</Order>
</Shipper>
Finally, step 6, we specify where we would like to save the XML document. '-----------------------------------------------------------------
'Customise (6 of 6). Enter path where XML document is to be saved:
'-----------------------------------------------------------------
vDocPath = "ShippingCosts2.xml"
Whilst these six steps appear complicated on the first reading, it is worth running the example application to see code in action. Figure 10, below, graphically depicts how the variables relate to XML elements and the ADO recordset fields.

Figure 10 - From ADO to XML
The six steps that we have just discussed are all that is required to help us create an XML DOM that represents the ADO recordset. As part of the download that accompanies this article there is a Visual Basic project in the Ex2 directory - running it will give you the chance to see how the six steps allow us to create an XML DOM from an ADO recordset. For the sake of clarity I have omitted the code fragment that creates the XML DOM - I will present it later in this article.
However, before we go on to analyze the XML DOM creation routine, it is worth looking at the XML document that it creates. Figure 11, below, presents the collapsed XML produced by the example application.

Figure 11 - hierarchy is maintained
As you can see, the hierarchy that we defined has been created: there is a root <ShippingCosts> element, followed by a collection of <Shipper> elements. Inside each <Shipper> element there is a collection of <Order> elements - Figure 12, below, presents the same recordset with the hierarchy emphasized.

Figure 12 - Hierarchy is maintained
Figure 13 presents the same recordset with ShipperID 1, and the first three OrderID elements expanded. A client-side application will find this XML is much easier to work with.

Figure 13 - Hierarchy is maintained through each level of master-detail
Now that we have looked at the theory behind the ADO to XML routine, let's take a look at the code that actually creates the XML DOM. The code fragment below uses the information we provided earlier to create a DOM tree based on the ADO recordset. This piece of code is generic, i.e. it is reusable - once you have configured the variables in the earlier six steps, this code can be used without further modification. As you go through the code, you may find that referring to figure 10 help paint a clearer picture.
'-----------------------------------------------
'Get the lowest level of the recordset hierarchy
'and declare arrays for elements:
'-----------------------------------------------
vLowestLevel = UBound(vEntity)
Dim xmlDocLevel()
ReDim xmlDocLevel(vLowestLevel)
Dim vIDTest()
ReDim vIDTest(vLowestLevel)
Set xmldoc = New DOMDocument
Set pi = xmldoc.createProcessingInstruction("xml", "version=""1.0""")
xmldoc.appendChild pi
Set xmlDocLevel(0) = xmldoc.createElement(vEntity(0))
xmldoc.appendChild xmlDocLevel(0)
'--------------------------------------------------------------
'Initialise ID test for each level (except root) to hash value.
'Ensure vHash can not match data in recordset.
'--------------------------------------------------------------
vHash = "#7&5%4d$s02#7&5%4d$s02psgr813$$%^qsx2"
For Level = 1 To vLowestLevel
vIDTest(Level) = vHash
Next
'---------------------------
'Loop through the recordset:
'---------------------------
Do While Not objRst.EOF
For Level = 1 To vLowestLevel
'---------------------------------
'Handle NULL values in ID columns:
'---------------------------------
If IsNull(objRst.Fields(vColUID(Level)).Value) Then
vIDValue = ""
Else
vIDValue = CStr(objRst.Fields(vColUID(Level)).Value)
End If
'-----------------------------------------------
'Create a new entity node if the ID has changed.
'Always create a new entity at the lowest level.
'-----------------------------------------------
If (Not vIDValue = vIDTest(Level)) Or (Level = vLowestLevel) Then
'----------------------------------------
'Initialise ID test for all lower levels:
'----------------------------------------
For lvl = (Level + 1) To vLowestLevel
vIDTest(lvl) = vHash
Next
'----------------------
'Create the new entity:
'----------------------
Set xmlDocLevel(Level) = xmldoc.createElement(vEntity(Level))
'-------------------------------------
'Set any attributes of the new entity:
'-------------------------------------
For idx = 0 To (objRst.Fields.Count - 1)
If (CInt(Left(aCol(idx), 1)) = Level) And (Right(aCol(idx), 1) = "A") Then
With objRst.Fields(idx)
xmlDocLevel(Level).setAttribute .Name, .Value
End With
End If
Next
'----------------------------------------
'Append as a child of the previous level:
'----------------------------------------
xmlDocLevel(Level - 1).appendChild xmlDocLevel(Level)
'----------------------------------------
'Append child elements to current entity:
'----------------------------------------
For idx = 0 To (objRst.Fields.Count - 1)
If (CInt(Left(aCol(idx), 1)) = Level) And (Right(aCol(idx), 1) = "E") Then
With objRst.Fields(idx)
Set xmlElement = xmldoc.createElement(.Name)
If Not IsNull(.Value) Then xmlElement.Text = CStr(.Value)
xmlDocLevel(Level).appendChild xmlElement
End With
End If
Next
'--------------------
'Update ID test text:
'--------------------
vIDTest(Level) = vIDValue
End If
Next
objRst.MoveNext
Loop
'------------------
'Save XML document:
'------------------
xmldoc.Save vDocPath
As part of the download that accompanies this article there is a Visual Basic project that demonstrates this code in action. The example is in the Ex2 directory.
Now that we have seen how to create XML from ADO, and maintain the hierarchy, creating an elementary XML Schema is the next step.
Let's take a look at how we can create an XML Schema for a simple ADO recordset, such as the recordset returned by:
SELECT * FROM Employees ORDER BY EmployeeID
Which, as you might expect returns the following employees:
1 Davolio Nancy 2 Fuller Andrew 3 Leverling Janet 4 Peacock Margaret 5 Buchanan Steven 6 Suyama Michael 7 King Robert 8 Callahan Laura 9 Dodsworth Anne
Creating a simple XML Schema for such a recordset is a fairly trivial task. First, we iterate over the fields collection using the name and type properties to build a collection of <simpleType> elements that represent the fields. Then we create a root element that holds all of the records. The code below does just that - it creates an XML Schema for a simple non-hierarchical recordset:
Private Sub ADOtoXMLSchemaSimple()
Dim vSQL, vDocPath As String
Dim sTargetNamespace, sDocumentNamespace As String
Dim sDocumentNSPrefix, sRootElement, sRootElementType As String
Dim objConn As ADODB.Connection
Dim objRst As ADODB.Recordset
Dim xmlDoc As MSXML2.DOMDocument
Dim idx As Integer
Set objConn = New ADODB.Connection
Set objRst = New ADODB.Recordset
'------------------------------
'Customise (1 of 4). Enter DSN:
'------------------------------
objConn.Open "DSN=Northwind"
Set objRst.ActiveConnection = objConn
'------------------------------
'Customise (2 of 4). Enter SQL:
'------------------------------
vSQL = "SELECT * FROM Employees"
vSQL = vSQL & " ORDER BY EmployeeID"
objRst.Open vSQL
'-----------------------------------------------------------------
'Customise (3 of 4). Enter path where XML document is to be saved:
'-----------------------------------------------------------------
vDocPath = "ShippingCostsSimple.xml"
'-----------------------------------------------------------------
'Customise (4 of 4). Provide defaults
'-----------------------------------------------------------------
sTargetNamespace = "urn:employees"
sDocumentNamespace = "urn:employees"
sDocumentNSPrefix = "e"
sRootElement = "Employees"
sRootElementType = "EmployeesType"
Dim xmlRoot As IXMLDOMElement
Set xmlDoc = New DOMDocument
Dim pi As IXMLDOMProcessingInstruction
Set pi = xmlDoc.createProcessingInstruction("xml", "version='1.0' encoding='UTF-8'")
xmlDoc.appendChild pi
Set xmlRoot = xmlDoc.createElement("xsd:schema")
xmlRoot.setAttribute "xmlns:xsd", "http://www.w3.org/2001/XMLSchema"
xmlRoot.setAttribute "targetNamespace", sTargetNamespace
xmlRoot.setAttribute "xmlns:" + sDocumentNSPrefix, sDocumentNamespace
xmlDoc.appendChild xmlRoot
Dim ctElement, stElement, rsElement, mLElement, mIElement As IXMLDOMElement
' First, define the simpleTypes for the recordset
For idx = 0 To (objRst.Fields.Count - 1)
Set stElement = xmlDoc.createElement("xsd:simpleType")
stElement.setAttribute "name", objRst.Fields(idx).Name + "Type"
' Sample derived type - strings usually have a fixed size...
If objRst.Fields(idx).Type = adVarWChar Then
Set rsElement = xmlDoc.createElement("xsd:restriction")
rsElement.setAttribute "base", "xsd:string"
Set mLElement = xmlDoc.createElement("xsd:maxLength")
mLElement.setAttribute "value", objRst.Fields(idx).DefinedSize
rsElement.appendChild mLElement
stElement.appendChild rsElement
Else
Set rsElement = xmlDoc.createElement("xsd:restriction")
Select Case objRst.Fields(idx).Type
Case adInteger: rsElement.setAttribute "base", "xsd:integer"
Case adDouble: rsElement.setAttribute "base", "xsd:double"
Case adNumeric: rsElement.setAttribute "base", "xsd:decimal"
Case adDate: rsElement.setAttribute "base", "xsd:date"
Case adUnsignedTinyInt: rsElement.setAttribute "base", "xsd:byte"
Case adDBTimeStamp: rsElement.setAttribute "base", "xsd:date"
Case adCurrency: rsElement.setAttribute "base", "xsd:float"
Case adNumeric: rsElement.setAttribute "base", "xsd:decimal"
' Not a complete list...
End Select
stElement.appendChild rsElement
End If
xmlRoot.appendChild stElement
Next
' Create the root element
Set stElement = xmlDoc.createElement("xsd:element")
stElement.setAttribute "name", sRootElement
stElement.setAttribute "type", sDocumentNamespace & ":" & sRootElementType
xmlRoot.appendChild stElement
Dim sqElement As IXMLDOMNode
Dim Element As IXMLDOMElement
' Create complexType
Set ctElement = xmlDoc.createElement("xsd:complexType")
ctElement.setAttribute "name", "EmployeesType"
' Create a <sequence><element>
Set sqElement = xmlDoc.createElement("xsd:sequence")
For idx = 0 To (objRst.Fields.Count - 1)
With objRst.Fields(idx)
Set stElement = xmlDoc.createElement("xsd:element")
stElement.setAttribute "name", objRst.Fields(idx).Name
stElement.setAttribute "type", objRst.Fields(idx).Name + "Type"
End With
sqElement.appendChild stElement
Next
ctElement.appendChild sqElement
xmlRoot.appendChild ctElement
'------------------
'Save XML document:
'------------------
xmlDoc.Save vDocPath
'--------
'Tidy up:
'--------
Set xmlDoc = Nothing
Set pi = Nothing
'------------
'ADO tidy up:
'------------
objRst.Close
objConn.Close
Set objRst = Nothing
Set objConn = Nothing
End Sub
If all went well, the code above creates the XML Schema presented in figure 14, below.

Figure 14 - simple XML Schema for Northwind's Employees table
Creating an XML Schema for an ADO recordset that maintains hierarchy is a little more involved.
Dim vSQL, vDocPath As String
Dim sTargetNamespace, sDocumentNamespace As String
Dim sDocumentNSPrefix, sRootElement, sRootElementType As String
Dim objConn As ADODB.Connection
Dim objRst As ADODB.Recordset
Dim xmlDoc As MSXML2.DOMDocument
Dim Level, vLowestLevel, idx As Integer
Set objConn = New ADODB.Connection
Set objRst = New ADODB.Recordset
'------------------------------
'Customise (1 of 7). Enter DSN:
'------------------------------
objConn.Open "DSN=Northwind"
Set objRst.ActiveConnection = objConn
'------------------------------
'Customise (2 of 7). Enter SQL:
'------------------------------
vSQL = "SELECT Shippers.ShipperID, Shippers.CompanyName,"
vSQL = vSQL & " Shippers.Phone, Orders.OrderID,"
vSQL = vSQL & " Orders.ShippedDate, Orders.Freight,"
vSQL = vSQL & " Orders.ShipCity"
vSQL = vSQL & " FROM Shippers INNER JOIN Orders"
vSQL = vSQL & " ON Shippers.ShipperID = Orders.ShipVia"
vSQL = vSQL & " WHERE (((Orders.ShippedDate) Is Not Null)"
vSQL = vSQL & " AND ((Orders.OrderID) BETWEEN 10200 and 10300))"
vSQL = vSQL & " ORDER BY Shippers.ShipperID, Orders.ShippedDate;"
objRst.Open vSQL
'------------------------------------------------------------------
'Customise (3 of 7). Name recordset hierarchy entities (no spaces):
'------------------------------------------------------------------
Dim vEntity(2)
vEntity(0) = "ShippingCosts" '(root)
vEntity(1) = "Shipper"
vEntity(2) = "Order"
'----------------------------------------------------------------
'Customise (4 of 7). Indicate which recordset column serves
'as unique identifier for each entity (zero-based column number).
'These columns should appear in the ORDER BY clause:
'----------------------------------------------------------------
Dim vColUID(2)
'vColUID(0) is not required.
vColUID(1) = 0
vColUID(2) = 0
'------------------------------------------------------------------
'Customise (5 of 7). Assign recordset columns to their entity-level
'and indicate if column is an attribute (A) or a child element (E)
'(use zero-based column numbers). E.g. "1A" creates an attribute of
'the level 1 entity and "2E" creates an element which is a child
'element to the level 2 entity:
'------------------------------------------------------------------
Dim aCol(7)
aCol(0) = "1A"
aCol(1) = "1A"
aCol(2) = "1A"
aCol(3) = "2A"
aCol(4) = "2E"
aCol(5) = "2E"
aCol(6) = "2E"
'-----------------------------------------------------------------
'Customise (6 of 7). Enter path where XML document is to be saved:
'-----------------------------------------------------------------
vDocPath = "ShippingCostsComplex.xml"
'-----------------------------------------------------------------
'Customise (7 of 7). Provide defaults
'-----------------------------------------------------------------
sTargetNamespace = "urn:shipping"
sDocumentNamespace = "urn:shipping"
sDocumentNSPrefix = "s"
sRootElement = "Employees"
sRootElementType = "EmployeesType"
The seven steps that are required to help us build XML Schema as very similar to the steps we performed when we created an XML document from the same ADO recordset. The additional seventh step is used to define the XML Schema specifics.
The remainder of the code in this article assumes the following variables exist and have been set:
'-----------------------------------------------
'Get the lowest level of the recordset hierarchy
'and declare arrays for elements:
'-----------------------------------------------
vLowestLevel = UBound(vEntity)
Dim xmlDocLevel() As IXMLDOMElement
ReDim xmlDocLevel(vLowestLevel)
Dim vIDTest()
ReDim vIDTest(vLowestLevel)
Dim atElement As IXMLDOMElement
Dim sqElement As IXMLDOMNode
Dim Element As IXMLDOMElement
Dim lastElement As IXMLDOMElement
Dim stElement As IXMLDOMElement ' <sequence>
Dim rsElement As IXMLDOMElement ' <restriction>
Dim mLElement As IXMLDOMElement ' <maxLength>
Set xmlDoc = New DOMDocument
Dim pi As IXMLDOMProcessingInstruction
Set pi = xmlDoc.createProcessingInstruction("xml", "version='1.0' encoding='UTF-8'")
xmlDoc.appendChild pi
Start building an XML Schema; firstly we create the <xsd:schema> element:
Set xmlDocLevel(0) = xmlDoc.createElement("xsd:schema")
xmlDocLevel(0).setAttribute "xmlns:xsd", "http://www.w3.org/2001/XMLSchema"
xmlDocLevel(0).setAttribute "targetNamespace", sTargetNamespace
xmlDocLevel(0).setAttribute "xmlns:" + sDocumentNSPrefix, sDocumentNamespace
xmlDoc.appendChild xmlDocLevel(0)
Next, we need to iterate over the ADO recordset's fields collection to extract the field names and data types:
' First, define the simpleTypes for the recordset
For idx = 0 To (objRst.Fields.Count - 1)
Set stElement = xmlDoc.createElement("xsd:simpleType")
stElement.setAttribute "name", objRst.Fields(idx).Name + "Type"
' Sample derived type - strings usually have a fixed size...
If objRst.Fields(idx).Type = adVarWChar Then
Set rsElement = xmlDoc.createElement("xsd:restriction")
rsElement.setAttribute "base", "xsd:string"
Set mLElement = xmlDoc.createElement("xsd:maxLength")
mLElement.setAttribute "value", objRst.Fields(idx).DefinedSize
rsElement.appendChild mLElement
stElement.appendChild rsElement
Else
Set rsElement = xmlDoc.createElement("xsd:restriction")
Select Case objRst.Fields(idx).Type
Case adInteger: rsElement.setAttribute "base", "xsd:integer"
Case adDouble: rsElement.setAttribute "base", "xsd:double"
Case adNumeric: rsElement.setAttribute "base", "xsd:decimal"
Case adDate: rsElement.setAttribute "base", "xsd:date"
Case adUnsignedTinyInt: rsElement.setAttribute "base", "xsd:byte"
Case adDBTimeStamp: rsElement.setAttribute "base", "xsd:date"
Case adCurrency: rsElement.setAttribute "base", "xsd:float"
Case adNumeric: rsElement.setAttribute "base", "xsd:decimal"
' Not a complete list
End Select
stElement.appendChild rsElement
End If
xmlDocLevel(0).appendChild stElement
Next
The code fragment above will generate the following XML Schema fragment:
<xsd:simpleType name="ShipperIDType">
<xsd:restriction base="xsd:integer"/>
</xsd:simpleType>
<xsd:simpleType name="CompanyNameType">
<xsd:restriction base="xsd:string">
<xsd:maxLength value="40"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="PhoneType">
<xsd:restriction base="xsd:string">
<xsd:maxLength value="24"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="OrderIDType">
<xsd:restriction base="xsd:integer"/>
</xsd:simpleType>
<xsd:simpleType name="ShippedDateType">
<xsd:restriction base="xsd:date"/>
</xsd:simpleType>
<xsd:simpleType name="FreightType">
<xsd:restriction base="xsd:float"/>
</xsd:simpleType>
<xsd:simpleType name="ShipCityType">
<xsd:restriction base="xsd:string">
<xsd:maxLength value="15"/>
</xsd:restriction>
</xsd:simpleType>
Whilst it is not rocket science, the code presented here builds an XML Schema that can be augmented and improved upon.
Creating the remainder of the XML Schema is a little more complicated. We have to iterate over the ADO recordset's fields collection again, this time referencing the simpleTypes that we have just identified.
' Create the root element
Set stElement = xmlDoc.createElement("xsd:element")
stElement.setAttribute "name", vEntity(0)
stElement.setAttribute "type", sDocumentNSPrefix + ":" + vEntity(0) + "Type"
xmlDocLevel(0).appendChild stElement
' Iterate over the nested items...
For Level = 1 To vLowestLevel
' Create complexType
Set xmlDocLevel(Level) = xmlDoc.createElement("xsd:complexType")
xmlDocLevel(Level).setAttribute "name", vEntity(Level - 1) + "Type"
' Create a <sequence><element>
Set sqElement = xmlDoc.createElement("xsd:sequence")
Set Element = xmlDoc.createElement("xsd:element")
Element.setAttribute "name", vEntity(Level)
Element.setAttribute "type", sDocumentNSPrefix + ":" + vEntity(Level) + "Type"
sqElement.appendChild Element
xmlDocLevel(Level).appendChild sqElement
' Create <element>
For idx = 0 To (objRst.Fields.Count - 1)
If (CInt(Left(aCol(idx), 1)) = Level) And (Right(aCol(idx), 1) = "E") And Level < vLowestLevel Then
With objRst.Fields(idx)
Set stElement = xmlDoc.createElement("xsd:element")
stElement.setAttribute "name", objRst.Fields(idx).Name
stElement.setAttribute "type", objRst.Fields(idx).Name + "Type"
End With
xmlDocLevel(Level).appendChild stElement
End If
Next
' Create <attributes>
For idx = 0 To (objRst.Fields.Count - 1)
If (CInt(Left(aCol(idx), 1)) = Level) And (Right(aCol(idx), 1) = "A") Then
With objRst.Fields(idx)
Set atElement = xmlDoc.createElement("xsd:attribute")
atElement.setAttribute "name", .Name
atElement.setAttribute "type", sDocumentNSPrefix + ":" + .Name + "Type"
End With
xmlDocLevel(Level).appendChild atElement
End If
Next
xmlDocLevel(0).appendChild xmlDocLevel(Level)
Next
' Handle the final entity
Set lastElement = xmlDoc.createElement("xsd:complexType")
lastElement.setAttribute "name", vEntity(vLowestLevel) + "Type"
Set sqElement = xmlDoc.createElement("xsd:sequence")
For idx = 0 To (objRst.Fields.Count - 1)
If (CInt(Left(aCol(idx), 1)) = vLowestLevel) And (Right(aCol(idx), 1) = "E") Then
With objRst.Fields(idx)
Set stElement = xmlDoc.createElement("xsd:element")
stElement.setAttribute "name", objRst.Fields(idx).Name
stElement.setAttribute "type", sDocumentNSPrefix + ":" + objRst.Fields(idx).Name + & -
"Type"
sqElement.appendChild stElement
End With
End If
Next
lastElement.appendChild sqElement
For idx = 0 To (objRst.Fields.Count - 1)
If (CInt(Left(aCol(idx), 1)) = vLowestLevel) And (Right(aCol(idx), 1) = "A") Then
With objRst.Fields(idx)
Set atElement = xmlDoc.createElement("xsd:attribute")
atElement.setAttribute "name", .Name
atElement.setAttribute "type", sDocumentNSPrefix + ":" + .Name + "Type"
End With
End If
Next
lastElement.appendChild atElement
xmlDocLevel(0).appendChild lastElement
The code fragment presented above creates the following XML Schema:
<xsd:complexType name="ShippingCostsType"> <xsd:sequence> <xsd:element name="Shipper" type="s:ShipperType"/> </xsd:sequence> <xsd:attribute name="ShipperID" type="s:ShipperIDType"/> <xsd:attribute name="CompanyName" type="s:CompanyNameType"/> <xsd:attribute name="Phone" type="s:PhoneType"/> </xsd:complexType> <xsd:complexType name="ShipperType"> <xsd:sequence> <xsd:element name="Order" type="s:OrderType"/> </xsd:sequence> <xsd:attribute name="OrderID" type="s:OrderIDType"/> </xsd:complexType> <xsd:complexType name="OrderType"> <xsd:sequence> <xsd:element name="ShippedDate" type="s:ShippedDateType"/> <xsd:element name="Freight" type="s:FreightType"/> <xsd:element name="ShipCity" type="s:ShipCityType"/> </xsd:sequence> <xsd:attribute name="OrderID" type="s:OrderIDType"/> </xsd:complexType>
If we put it all together, Figure 15 presents the XML Schema that can be used to validate our hierarchy-aware XML. The code for this example can be found in the Ex3 directory.

Figure 15 - XML Schema that allows us to validate hierarchy
This has been a very busy article. Hopefully you have seen the merits of XML Schema, and have gained an understanding of how we can begin to use it in our XML-based applications. An article of this size could not expect to cover all aspects of XML Schema; after all, the combined recommendations are some 400 pages of A4. Using the code presented in this article, you can create "first-pass" schemas for your ADO recordsets. In reality, I use these schemas as a starting point, i.e. as a timesaving process.
XML Schema (1st May 2001 W3C Recommendation): Part 0: Primer: http://www.w3.org/TR/xmlschema-0/ Part 1: Structures: http://www.w3.org/TR/xmlschema-1/ Part 2: Datatypes: http://www.w3.org/TR/xmlschema-2/
MS XML 4: http://www.microsoft.com/downloads/release.asp?ReleaseID=31333 (July 2001 preview)
XML Tutorials: http://www.msxml.com
IBM Schema Quality Checker: http://alphaworks.ibm.com/tech/xmlsqc/
Sun's Java Runtime Environment (JRE) 1.3: http://java.sun.com/j2se/1.3/jre/ (required for the Schema Quality Checker)
XML Spy: http://www.xmlspy.com
Professional XML Schemas, Wrox Press, ISBN 1861005474
A Library of Regular Expressions for Form Validation: http://www.asptoday.com/content/articles/19990629.asp
JScript Primer 3/4: Regular Expressions: http://www.asptoday.com/content/articles/19990330.asp
String Manipulation and Pattern Testing with Regular Expressions: http://www.asptoday.com/content/articles/19990505.asp
|
| |||||||
|
|
|||||||||
| ASPToday is brought to you by
Wrox Press (http://www.wrox.com/). Please see our terms
and conditions and privacy
policy. ASPToday is optimised for Microsoft Internet Explorer 5 browsers. Please report any website problems to webmaster@asptoday.com. Copyright © 2001 Wrox Press. All Rights Reserved. |