Delphi Clinic C++Builder Gate Delphi Notes Weblog C#Builder Visions Delphi for .NET
Dr.Bob's Delphi Notes Dr.Bob's Delphi Clinics
 Dr.Bob Examines... #76
See Also: other Dr.Bob Examines columns or Delphi articles

This article was first published in the UK Developers Group Magazine.

XML Documents, Schemas and Validation
How to produce an XML Schema (and do validation) when all you have is the XML Document

In this article, I'll show you how you can generate the XML schema from an XML document with only a few lines of code in Delphi (also applicable to Kylix or C++Builder).

Delphi 6 and 7 Enterprise include the XML Mapping Tool, which can map XML documents to datasets, but is also used by me (from time to time) to produce the XML Schema for a given XML document. Since the process of loading an XML document into the XML Mapper and producing the XML schema was fun at first, but a bit time consuming if you have lots of XML documents (not to mention boring), I was looking for another way to produce XML schemas for XML documents that do not have an XML schema produced already. In my search for a helpful technique, I noticed the XMLDataToSchema unit (in the Source\Xml directory) contains the TXMLDataImporter and registers it as an XML Schema Translator Factory. Even more interesting is the XMLSchema unit itself, which defines the IXMLSchemaDoc interface, as well as a number of schema helper functions such as GetXmlSchema, LoadXmlSchema, and LoadXmlSchemaStr. GetXmlSchema works on an IDOMDocument interface argument, in case you already have an opened XML document (component). LoadXmlSchema is able to load an XML document from filename, so is easier to use if you do not already have an open XML document (component) available. The LoadXmlSchemaStr function doesn't get the filename as argument, but rather the entire XML document itself as long string argument (this can be useful when you receive a dynamic XML document via a socket or memory stream for example).
In my case, all I needed was the LoadXmlSchema function, pass it a filename, and then work on the loaded XML schema/ For example saving it to disk again (using the same filename, but this time with the .xsd extension). LoadXmlSchema returns an IXMLSchemaDoc interface, and I only need to call the SaveToFile method in order to save the XML schema to disk.
The source code of a small console application that produces an .XSD XML schema based on an XML document is shown below:

  program XML2XSD;
  {$APPTYPE CONSOLE}
  uses
    SysUtils, ActiveX,
    XMLDataToSchema, XMLSchema;
  var
    XML,XSD: String;
  //XMLSchemaDoc: IXMLSchemaDoc;
  begin
    if ParamCount = 0 then
    begin
      writeln('Usage: XML2XSD XML [XSD]')
    end
    else // convert
    try
      CoInitialize(nil);
      XML := ParamStr(1);
      XSD := ParamStr(2);
      if XSD = '' then
        XSD := ChangeFileExt(XML,'.xsd');
      LoadXmlSchema(XML).SaveToFile(XSD)
  //  XMLSchemaDoc := LoadXmlSchema(XML);
  //  XMLSchemaDoc.SaveToFile(XSD);
    finally
  //  CoUninitialize
    end
  end.
Note that I've placed four lines in comments. The first three explicitly used the XMLSchemaDoc variable of type IXMLSchemaDoc, but I quickly noticed that you can combine them by calling LoadXmlSchema (which produces an IXMLSchemaDoc as result) and then apply the SaveToFile method to the resulting interface. I only needed to pass the filename of the XML document to LoadXmlSchema, and the filename for the resulting XML schema to the SaveToFile method.
The fourth line that I had to place in comments is the CoUninitialize call. In order to work with XML documents inside console applications, you have to call CoInitialize, but somehow I'm unable to call CoUninitialize without getting access violations, so I've had no choice but to leave the call to CoUninitialize behind (feel free to contact me in case I've done something wrong here, but it seems to work just fine now).
The resulting console / command-line application is used on a frequent basis by me, generating XML schemas when I need them for the collection of XML documents that I have to work with.

Another piece of code that I sometimes use if a way to validate an XML model against its schema. This can be done with the validateOnParse property of the MSXML.DOMDocument, as follows:

  var
    XML, XSDL: Variant;
  begin
    XSDL := CreateOLEObject('MSXML2.XMLSchemaCache.4.0');
    XSDL.validateOnLoad := True;
    XSDL.add('','MySchema.xsd'); // 1st argument is target namespace
    ShowMessage('Schema Loaded');
    XML := CreateOLEObject('MSXML2.DOMDocument.4.0');
    XML.validateOnParse := True;
    XML.resolveExternals := True;
    XML.schemas := XSDL;
    XML.load('file.xml');
    ShowMessage(XML.parseError.reason);
  end.
These two little code snippets may help you when working with XML documents in Delphi for Win32.


This webpage © 2006-2008 by Bob Swart (aka Dr.Bob - www.drbob42.com). All Rights Reserved.