OLE Automation for XML

fabrice.marguerie@cogisoft.fr
Download version 04/27/1999


Introduction

Currently, to access the content of an XML document, we can use parsers providing a DOM interface (Document Object Model).

Let's take a look at the following document (Books.xml):

<bookstore>
  <book genre="autobiography" count="15">
    <title>The Autobiography of Benjamin Franklin</title>
    <author>
      <first_name>Benjamin</first_name>
      <last_name>Franklin</last_name>
    </author>
    <price>8.99</price>
  </book>
  <book genre="novel">
    <title>The Confidence Man</title>
    <author>
      <first_name>Herman</first_name>
      <last_name>Melville</last_name>
    </author>
    <price>11.99</price>
  </book>
  <book genre="philosophy" count="2">
    <title>The Gorgias</title>
    <author>
      <name>Plato</name>
    </author>
    <price>9.99</price>
  </book>
</bookstore>

In case we use Microsoft's XML parser (msxml.dll version 2), we are given COM interfaces (IXMLDOMDocument, IXMLDOMNode, ...) to handle the XML document as a tree. These interfaces are not always adapted for some processings where a simpler system would be better. In some cases, we would like to be able to use calls like book.author.last_name to access the content of this document like fields of a structure.

We can achieve this through the mecanism that handles OLE Automation calls in Delphi.
To understand this mecanism you can take a look at the StringsAuto sample that enables you to handle variables contained in a TStrings object as a structure's fields.
This way, if a TStrings object contains the following text:

Var1=abc
Var2=def

we are able to access the value of Var1 with the call vStrings.Var1 where vStrings is a variant containing the TStrings object.


Use

We will now see how to implement this functionality with Delphi 4 and Microsoft's XML parser (Internet Explorer 5 is required).
The code required is provided in the XMLAuto unit.

When a value is assigned to the Visible property of the Word object in the following example, we use an OLE Automation call handled by the VarDispInvoke procedure of Delphi's ComObj unit.

var
  Word: Variant;
begin
  Word := CreateOleObject('Word.Application');
  Word.Visible := True;
end;

Delphi provides the VarDispProc variable that let us set up a new handler for OLE Automation calls. We will use this mecanism to handle calls against an XML document and its content.

var
  Document: Variant;
begin
  Document := CoDOMDocument.Create;
  Document.load('Books.xml');
  ShowMessage(Document.bookstore.book.author.last_name.text);
end;

This kind of notation doesn't exclude calls to DOM:

ShowMessage(Document.bookstore.childNodes[1].author.last_name.text);

This kind of call needs the global variable EnableDOM to be set to True (default). This Way we can access DOM's methods or properties such as childNodes here.


We can also use the following call that references the second book:

ShowMessage(Document.bookstore.book[1].author.last_name.text);

To obtain a more compact code, it's possible to set the EnableDefaultProperty variable to True, implicating an implicit reference to the text property.

ShowMessage(Document.bookstore.book.author.last_name);

Of course modifications to the XML document are possible:

Document.bookstore.book.title.text := 'test write 1';

even if EnableDefaultProperty = True:

Document.bookstore.test1 := 'test write 3';

We are also able to dynamically create elements if they don't exist:

Document.bookstore.test1.text := 'test Write 2';

The global variable AutoCreateElement specifies if this mecanism of automatic creation is activated.


AutoCreateElement is compatible with EnableDefaultProperty:

Document.bookstore.test2 := 'test write 4';
Document.bookstore.test2[1] := 'test write 5';

Limits

Name conflicts

In case EnableDOM = True, we can meet conflicts between elements/attributes of the XML document and methods/properties of the XML DOM.
Assume an element element containing a text sub-element:

<element>
  <text>exemple de texte</text>
  <autre_sous_element>de conflit<autre_sous_element>
</element>

The call element.text will always return "exemple de texte".

In some cases we want to access to the text "exemple de texte de conflit".
This is usually achieved with EnableDOM = True and the use of the text property of the DOM node. Here, there is a conflict between the text element and the text property.
A workaround for this problem is to play on character's case. So, the call element.Text will return "exemple de texte de conflit" if the DOM is enabled.

Symbols

It is not possible to access elements or attributes containing symbols not supported by Delphi for identifiers (eg: last-name is invalid, last_name is valid). To access elements or attributes whose name includes a symbol, the DOM must be used.

Attributes/Elements

How to access an attribute X where there is also an element X?

Automated creation

Elements are created automatically. How to create attributes? Use a new configuration flag?


Notes

It is highly recommended to use intermediate variables not to slowdown processings.

For example, to access information on a book, like this:

ShowMessage(Document.bookstore.book[1].author.last_name.text);
ShowMessage(Document.bookstore.book[1].author.first_name.text);

it's better to use a Book variable of type Variant:

Book := Document.bookstore.book[1];
ShowMessage(Book.author.last_name.text);
ShowMessage(Book.author.first_name.text);

Reference

Perspectives

The same kind of work can be done for datasets (TTable, TQuery, ...) to enable direct access to fields: tblCustomer.LastName := 'toto'.

For more information, to submit bugs, for suggestions, don't hesitate to contact me: fabrice.marguerie@cogisoft.fr

To get latest version, go to http://www.sophia-group.com/tech or http://www.sophia-group.com/downloads