Data Exchange using XML and Delphi
Author: Deepak Shenoy
Introduction
- Introduction to XML
- Where does XML fit in?
XML
and Delphi
- Stock Demo - Steps 1 to 4
- Converting Existing Applications to use XML
Data
communication using XML
- Interoperability with other systems
- Communication Demo
Technologies
that use XML
- Internet Express
- BizTalk
- SOAP - Simple Object Access Protocol
Conclusion
References
Appendices
- Listing
1-Stock View Application
- Listing
2-Stock Server
- Listing
3-XML in a Client Dataset
-------------------------------------------------------------------------------------------------------
Introduction
You
have a client-server application. It's the age old concept
- a set of forms on the client, a database on the server.
Data access through ADO or BDE. Works great on a desktop or
on a corporate intranet.
Now, you have clients that want to access data "remotely"
- across a WAN, or the Internet. So you're working on building
a remote client for remote users. Then some specifications
change, and you end up maintaining TWO versions - the remote
app and the "local" application. Now someone wants
a browser based client, and suddenly maintenance is a nightmare!
You obviously need a solution that reuses the code on your
client, yet allows you to remotely access your server. There
are many solutions that currently exist: I will not delve
into all of them. I'll talk about how you can use Extensible
Markup Language (XML) to exchange data between your server
and client - a solution that I will demonstrate to be scalable
to remote access, with reduced code-maintenance.
I will not demonstrate any proprietary code or even try
to sell you any software - the aim of this article is
to tell you how you can use XML to make your applications
open and scalable. By open, I mean your application can "talk"
to similar other applications, and by scalable I mean you
can make remote clients, browser based clients etc. easier
to develop.
I will compare my idea with Internet Express - the
solution provided with Delphi 5. As it stands today,
Internet Express does the job of getting your data on to a
browser - a remote access solution. But there is one thing
you lose - the ability to define your XML yourself, to import
data from other XML - one of the main benefits of XML.
All of the source code accompanying this article is free
for you to use, commercially or otherwise. Click
here to download the source code.
XML– Introduction to XML
What is XML? It's a language. It is written is plain text
- in a certain format. The "format" is used to structure
the data so it makes it more readable than your average shopping
list. Let's take an example: A list of books that you would
want to display, for instance, could be shown in a paragraph
listing the book name, the author and an abstract. In XML,
you would describe such a listing as:
<BOOK Name="Tomorrow
won't be like yesterday">
<Author> Pres Enttense </Author>
<Abstract>
If you're worried about not getting enough done today,
you're wasting your time. Don't lose your hair because
your boss needs to play golf tomorrow. Golf can wait.
It's a boring game anyway.
</Abstract>
</BOOK> |
Figure 1
The parts between the "<" and the ">"
is a "tag". Which means it isn't something that's
part of the data itself, but something that describes what
the data means. The piece of XML above reveals that there's
a book named "Tomorrow won't be like yesterday",
whose author is Pres EntTense and there's an abstract given.
Now you can extract the fields you want and present it in
your application. But before we go ahead, let's discuss how
XML is structured.
XML Structure.
The prolog.
First, you need to be able to say "what follows is an
XML document". Otherwise, a program wouldn't be able
to tell it from anything else. You would add a prolog like
so:
The only difference between the prolog and an XML tag is
the "?" that's between the angle brackets.
The Document Element
Each XML document describes something - and this something
is depicted as a base tag inside which all the other data
goes in. In our example above, a book was the document element,
so the XML was surrounded by <BOOK>
and </BOOK>. If we had two or more
books we'd have to use:
<BOOKS>
<BOOK>
...
</BOOK>
<BOOK>
...
</BOOK>
</BOOKS> |
and <BOOKS> is the document element
here. Every XML document needs one and exactly one document
element.
Document Content
A document can various types of content
1. Elements
2. Attributes
3. Comments
Elements
A "tag" is an element. In our example above, BOOK,
Author, Abstract are XML elements. Each element in XML should
have starting and ending tags - for any tag X there must be
a <X> and a </X> with the </X> coming after
the <X>. In HTML, such a restriction is not imposed,
so we sometimes find <p> tags which don't have a corresponding
</p> tag.
Text between the element start and end tags is called the
CDATA of the element. In,
<Soup> Today's special is the lobster soup </Soup>
The Soup element is described by the text between the tags,
which is the CDATA. Sometimes, it's not necessary to have
CDATA, like in <BR> tags in HTML. So one has to write
<BR></BR>
everytime, which is quite redundant. XML provides an abbreviated
way to handle this: for elements with no CDATA, it's valid
to start and end an element with a slash before the ending
>, like <BR/>
Attributes
Attributes are properties of elements, which come inside
the element tag, just after the element name. In,
<Product Name="Borland Delphi" Version="5"/>
Name and Version are attributes
of the element Product. The attributes have values, as seen
above. This kind of granularity can also be achieved by using
sub-elements, since the above XML could be written as:
<Product>
<Name>Borland Delphi</Name>
<Version>5</Version>
</Product> |
There's no real rule of thumb about which to use when. A
general guideline would be to use attributes when the text
is small and manageable, and sub-elements for larger texts
or when the text may need to contain other sub-elements.
Comments
Comments in XML are just like HTML comments, starting with
"<!--" and ending with "-->". Anything
in the middle is ignored (even if there are any element tags
in there, they will not be processed)
Document Type Definition (DTD)
XML, as we've seen, is just structured text. Since it is so,
it needs to be parsed, and many parsers have been written
for this purpose. Some ground rules need to be followed, such
as:
a) always end an element tag. You can use
only <TAG/>, <TAG></TAG> or <TAG>content</TAG>.
b) Maintain that there is only one document
element surrounding the XML.
c) XML is case sensitive. So <Element>
is not the same as <ELEMENT>
d) Use quotes when giving attribute values.
Although
<img src=myimg.gif>
is valid in HTML, it is not in XML. It should be:
<img src="myimg.gif"/>
These ground rules form the basis for "well
formed XML". But If you need to even
define the structure of the XML, like which tag should follow
which, what tags are allowed etc. you will need to describe
that in a separate document, called a Document Type
Definition (DTD).
Document Type Definition (DTD).
A DTD is also a text document. A DTD for our example above
would be:
<!ELEMENT BOOK (Author
| Abstract)>
<!ATTLIST BOOK Name CDATA>
<!ELEMENT Author (#PCDATA)>
<!ELEMENT Abstract (#PCDATA) #REQUIRED> |
The first <!ELEMENT part defines an XML
Element, in this case the <BOOK> XML
element. The latter part of the same line (Author|Abstract)
says that a BOOK tag contains either Author
or Abstract tags. The <!ATTLIST part defines
the Name attribute of the BOOK tag.The rest
of the tags define the other elements in the XML.
An XML document can specify the DTD that defines it - like:
<?xml version="1.0"?>
<!DOCTYPE BOOK SYSTEM "book-structure.dtd">
<BOOK>
...
</BOOK> |
Note:(book-structure.dtd is a text file containing
the dtd).
An XML that is well formed AND conforms to a DTD, it is said
to be "valid XML". The XML that is used in this
paper is not valid XML - DTDs have not been used anywhere,
for clarity and space.
XML Schemas
An aside: DTDs were the first standard for defining XML. But
a different parser has to be written for parsing a DTD, since
a DTD does not conform to XML rules. XML schemas
is a newer (although not yet a standard) approach, which uses
valid XML to define another XML document. The format is different
from a DTD, and supports some more data types. I won't delve
into XML Schemas in this paper.
XML and databases
This demonstrates a structured set of data in plain text –
so you can extract out the fields you want and present it
in your application. Just like a table in a database, without
the baggage of logging in, security, connection management
and the like. And it's all plain text, so it's readable. XML
is really extensible, unlike a table in a database - you can
extend an XML to show master-detail relationships quite easily:
<Customer name="International
Operations">
<Order No="1110">
<Item Code="500-1100" Quantity="5">
</Item>
<Item Code="200-1000" Quantity="10">
</Item>
</Order>
<Order No="400">
<Item Code="510-1100" Quantity="3">
</Item>
<Item Code="200-1000" Quantity="25">
</Item>
</Order>
</Customer> |
Figure 2.
This is a list of orders and the corresponding items. A file
like this can be transmitted to a client over the Internet
by email or using TCP/IP sockets. This can be used just like
a database by the client, since the fields are neatly marked
by tags. All of this, without the client having to own a license
of the database server. Don't get me wrong: XML is not meant
to replace database servers. It's going to complement them.
You will still need optimized data storage, server side indexes,
stored procedures and security - something XML will not address
in the form it is now. (I wonder if this statement will remain
sensible by the time this article is presented)
XML data can easily be passed over a network to a client
who could choose to show it in a set of TDBGrids in a fat-client
application. The same information can be formatted and shown
in a Web Browser. (Internet Express does this job really well)
You can even send XML to a customer-his payment system can
now integrate with yours, all he needs to do is parse the
XML, get a list of orders/items, confirm receipt, and generate
payment information - all the customer needs to do is to sign
the check. (If it were so easy...) And finally, the customer
sends the payment information to you - again in XML - so you
can directly import the details into your application for
your information and later reconcile it with your bank statements.
Where does XML fit in?
Your client-server application consists of data being exchanged
between your client and server. Data is exchanged in a format
that you would not be aware of, which limits your application's
scalability. If you used XML as the data exchange format,
you could easily scale your client to a remote client –
by simply implementing a data transport mechanism. You could
use TCP/IP and have users accessing the server over the Internet,
for instance.
In many enterprises, most user access consists solely of
data reads. Instead of having all your users run client applications
capable of reading and writing data, you can "web-enable"your
server application by developing a web server application
– an MTS component or an Enterprise Java Bean –
that would connect to your server using TCP/IP or DCOM, get
data in XML format and format it to emit HTML. This way all
users can just log on to the Intranet site for reads. Later,
you can even have other applications use this method to read
data from the same server application and you will only need
to understand the XML format returned. This makes it quite
simple to build distributed applications.
Does this require that you give up developing applications
using the BDE/ADO ? Or clients using data-aware controls?
No. You will still use BDE/ADO on your desktop and Intranet
Multi-tier applications.
I will show you how you can allow a client to access data
on your server using XML. Which means that your server will
need to have some components which can give information in
XML and perhaps read information from a client in XML too.
How do you do this in Delphi?
Let's start looking at some code now. First, XML is plain
text, so to make any real sense out of it,
XML needs to be parsed. Delphi 5 comes with a basic XML parser
(in InternetExpress, but you cannot use it for other purposes),
and there are many free and commercial XML parsers available.
I'll use the redistributable XML parser that comes with Microsoft
Internet Explorer 5. It's not the best but it's definitely
easy to start with since it supports COM interfaces. I've
started off importing the Parser's interface from MSXML.DLL
in the Windows System directory.
A small introduction to the
functions involved:
1. CreateOleObject('Microsoft.XMLDOM')
- Creates the MSXML Object. We could even use CoCreateInstance,
or the CoXMLDocument.Create in the imported Pascal file.
2. .Load(FileName), .Save(FileName)
- Loads from/saves to the file named filename.
3. .LoadXML(XMLString) - Loads a
string in XMLString and parses it.
4. .XML - The XML as a string
5. .createNode( type, nodename, namespace)
- creates a node like <namespace:nodename> </namespace:nodename>.
if the type is NODE_ELEMENT. You can
specify NODE_ATTRIBUTE to add an attribute
to a node like <item> can become <item code="111-0000">.
"code" is an attribute.
(Similar items: createElement, createAttribute
) |
Figure 3.
I'll start with a demonstration here and extend
it. First, I'll create an application that will show some
stock quotes in a List View. (I've used some of the samples
that Microsoft ships and converted them to Delphi. This is
one of them, but I've extended it) The stock data is in an
XML file.
Stock Demo
A small stock application that gets quotes from
a server application - we'll use XML to communicate between
the applications.
Step 1.
Let's now start with creating a small application that displays
stock quotes to a user. The stock quotes are presumed to be
in XML, such as:
<quotes>
<quote symbol="MSFT" price="90"
change="61" open="29" volume="24370"
lastupdate="12/28/99 18:57:07"/>
// more quotes here...
</quotes>
|
Figure 4.
We'll drop a listview on the screen that shows
each quote in a different row.A bitmap indicates whether the
stock is up or down. The form looks like this:
Figure 5.
Listing 1 shows the code that
creates the XML Parser, opens an XML file, parses it using
the XML parser and displays it on screen. The XML file that
was loaded was :
<quotes>
<quote symbol="MSFT" price="90"
change="61" open="29" volume="24370"
lastupdate="12/28/99 18:57:07"/>
<quote symbol="INFY" price="10"
change="-10" open="6" volume="31101"
lastupdate="12/28/99 18:57:07"/>
<quote symbol="INPR" price="93"
change="45" open="48" volume="19629"
lastupdate="12/28/99 18:57:07"/>
</quotes> |
Figure 6.
The code that parses the document is :
lstNodes := FDocument.selectNodes('//quote');
// all elements of type <quote>
node := lstNodes.nextNode as IXMLDOMElement; // go through
all the nodes
while node<>nil do begin
vVal := node.getAttribute('price'); // price
is an attribute of <quote>
liStock.SubItems.Add(vVal);
...
end; |
Figure 7.
We've now got our first XML based client screen
up and running
Step 2.
Let's move to the server. Obviously, we're going to have to
lookup these stock quotes someplace. I'll make a dummy quote
server now, which just looks up stock tickers from a database
and generates random values for the stock prices.
I've created a table called Stocks with the structure in Figure
8.
Column |
Type |
SYMBOL |
VARCHAR(10) |
PRICE |
FLOAT |
CHANGE |
FLOAT |
VOLUME |
FLOAT |
OPEN |
FLOAT |
Figure 8.
This stock quote server will have a port 3580
open for requests. A server socket will listen to requests,
which have to be in XML of the form:
<quotes>
<quote symbol="MSFT"/>
<quote symbol="INPR"/>
</quotes> |
Figure 9.
The output will be XML (just as shown in Figure
6.), sent back to port 3580. This is pretty plain and simple.
The server form is shown in Figure 1, and the code in Listing
2.
Figure 10.
The ADO Connection connects to
an Access .MDB file that holds all the stock symbols
qrySymbol is an ADO Query that does "SELECT * FROM STOCKS
WHERE SYMBOL=:SYMBOL"
The server socket listens for connections on port 3580. The
code is given in Listing 2.
Step 3.
Let's now use a TClientSocket to send requests to
this port at the server and receive the data.
var
szRec : array[0..99] of char;
st : TWinSocketStream;
begin
szXML := FDocument.xml + #13#10 + #13#10; // like
an End of input marker
with ClientSocket1 do
begin
Open;
Socket.SendText(szXML);
st := TWinSocketStream.Create(Socket,
2000);
if st.WaitForData(1000)
then
szXML := Socket.ReceiveText;
st.Free;
if szXML <> ''
then
FDocument.loadXML(szXML);
ShowXML;
CLose;
end;
end; |
Figure 11.
A screen shot of the update process is shown,
with the refreshed data in view (Figure 12.)
Figure 12.
4. Showing the data on a Web browser : Here's
code for an ISAPI DLL that will get requests from a web browser,
and show the results to the user in a browser.
var
szRec : array[0..99] of char;
st : TWinSocketStream;
szXML : String;
begin
szXML := ' ' + #13#10 + #13#10;
with ClientSocket1 do
begin
Open;
Socket.SendText(szXML);
st := TWinSocketStream.Create(Socket,
2000);
if st.WaitForData(1000)
then
szXML := Socket.ReceiveText;
st.Free;
CLose;
end;
Response.StatusCode := 200;
Response.ContentType := 'text/xml';
Response.ContentLength := Length( szXML
);
Response.Content := szXML;
Handled := TRUE;
end; |
Figure 13.
A screen shot will give you an idea of what
kind of output you can expect:
Figure 14.
Note: This is Internet Explorer
5, which can show XML.
This is a very simple example of what we can
do with XML. If we have more stock servers that return quotes
in the XML format in Figure 2, the client not depend on a
particular server at all - just point the application to a
different server if one is down for maintenance.
What if we wanted multiple users to edit data?
Just place some code at the server that will parse the input
XML and update the database - You might need to add validations,
security etc. but the model is in place.
Here's the architecture of this application
in a nutshell.
How do you convert currently existing applications?
You can't modify your existing applications to use this strategy,
it'll take way too much time. Consider extending your current
application to use XML. So you'll start off with exposing
parts of your data using XML.
A large part of enterprise applications consists of only
reads - there are much fewer instances of writing data to
a database than to read from it. For plain reads, just run
a query on the database and make XML out of it. You can do
that quite easily by :
function MakeTag( TagName,
Value : String ) : string;
begin
Result := '<' + TagName + '>' + Value +
'</' + TagName + '>';
end;
function MakeXML( Dataset : TDataset ) : string;
begin
Result := '';
if (not Dataset.Active) or
(Dataset.IsEmpty) then Exit;
Result := Result + '<' + Dataset.Name + '>';
Dataset.First;
while not Dataset.EOF do
begin
Result := Result + '<RECORD>';
for i := 0 to Dataset.Fields.Count-1 do
Result := Result + MakeTag(Dataset.Fields[i].Name,Fields[i].Text);
Result := Result + '</RECORD>';
Dataset.Next;
end;
Result := Result + '</' + Dataset.Name + '>';
end; |
Figure 16.
This doesn't cover BLOB fields or Memo fields etc. But for
float fields (use a DisplayFormat),
String/character fields and Integer fields this works quite
well.
There is a possibility of an XML Dataset that could emerge
soon. A TDataset descendant that will give you a way to show
parts of XML in datafields. Then, you can start using XML
to display your data easily. Of course, since XML is plain
text, we still have the issue of speed with large datasets
- but since you control the interaction between the server
and the client, you can send data in chunks or even split
it into more manageable parts.
Interoperability with other systems
With an XML based implementation, you have one more advantage.
Your applications can communicate with other packages on potentially
different platforms. If two airline companies were to collaborate
– so their customers could have a single point of purchase.
Lets say they used different software for invoicing. To avoid
hauling one airline company on to a common invoicing system,
you could design an application that would convert their data
to a common XML format – and use this format in the
common ticket order system. There are innumerable advantages
to this approach – a saving in time and money is only
one of them.
DEMO
I'll demonstrate two applications that use different data
formats for internal storage. I've added XML support to both
the applications so they can communicate with each other –
they also have support for more such applications to integrate
with them.
Note:
All the source code is available with this document, suitably
zipped.
First, take a look at the screen shot of an Internet Site
that shows the list of Customers of Company ABC, which is
a reseller of Product T, made by Company XYZ. This is XML,
formatted for browser viewing using XSL - Extensible Stylesheet
Language . (I will not cover XSL in this paper)
Figure 17.
Company XYZ direct sells Product T too, but it needs to see
all the customers of the product. I've built an application
that reads data from XYZ's internal database and shows it
in a Grid. In addition, it reads the XML from the Company
ABC's web site, and shows the customers in the same grid,
shown in Red. (I've sorted the list by Company Name).
Figure 18.
Technologies and Products that use XML
InternetExpress
InternetExpress is an XML solution by Inprise. With the enterprise
version of Delphi 5, (and C++ Builder 5, I guess) you can
create web clients for your multi-tiered applications. InternetExpress
comes with special Javascript libraries which can be used
on any browser that supports Javascript. These libraries contain
an XML parser too, but you may not use these for non-MIDAS
applications.
You can create a regular Web application and drop a TXMLBroker
on it. This acts like a ClientDataset, except it requests
packets in XML instead. Then the XML is sent to the client,
along with the Javascript XML Libraries. The XML is like this:
<DATAPACKET Version="2.0">
<METADATA>
<FIELDS>
<FIELD attrname="OrderNo" fieldtype="r8"/>
<FIELD attrname="CustNo" fieldtype="r8"/>
<FIELD attrname="SaleDate"
fieldtype="dateTime"/>
....more fields ...
<FIELD attrname="OrderItems"
fieldtype="nested">
<FIELDS>
<FIELD attrname="OrderNo"fieldtype="r8"/>
<FIELD attrname="ItemNo"
fieldtype="r8"/>
...more fields...
</FIELDS>
<PARAMS DEFAULT_ORDER="16385"
PRIMARY_KEY="1 2" LCID="1033"/>
</FIELD>
</FIELDS>
<PARAMS MD_FIELDLINKS="22 1 1" LCID="1033"/>
</METADATA>
<ROWDATA>
<ROW OrderNo="1014" CustNo="1645"
SaleDate="19880525" ShipDate="19880526"
EmpNo="144" ShipVIA="Emery"
Terms="Net 30" PaymentMethod="Credit"
ItemsTotal="134.85" TaxRate="0"
Freight="0" AmountPaid="134.85">
<OrderItems>
<ROWOrderItems OrderNo="1014"
ItemNo="1" PartNo="7612" Qty="4"
Discount="0"/>
</OrderItems>
</ROW>
...more rows...
</ROWDATA>
</DATAPACKET> |
Figure 19.
The XML libraries at the client allow you to see the data,
even modify (insert, update, delete) data without a round
trip to the server. In effect, the XML libraries at the client
cache these updates. You can then apply all the updates at
one shot.
You can change the Web page layout - the HTML. But you have
no control over the XML format. You cannot add custom tags,
data or attributes directly.
Is it possible to connect to other systems or have other
systems connect to your application?
Not unless they use MIDAS too. The XMLBroker component works
with the IAppServer interface, an integral
interface of MIDAS. Yes, you could have a customer read XML
directly from your web server application using the format
given above. (I'm still testing this theory) But XML isn't
at its productive best, in my opinion.
If you are developing on MIDAS, try InternetExpress: it will
save a lot of work if you want to have web clients.
BizTalk
In this context, Microsoft's Biztalk initiative (www.biztalk.org)
is worth a mention. In Biztalk, you can define the XML definitions
of your business objects. A worldwide definition library then
allows to you to interact with other such publishers - now,
with BizTalk server, you can have a PeopleSoft implementation
connect to an SAP implementation etc. It's something worth
looking at.
SOAP - Simple Object Access Protocol
There's a new kind of server coming up-The SOAP server. This
have nothing to do with what comes on TV - it's much easier
to understand, at the very least.
This concept came about because one had to distribute client
access across machine and location boundaries. When a firewall
steps in, it's usually more than hell to configure a DCOM
based multi-tier application. One way out is to use the HTTP
port - port 80. This is generally let through by firewalls.
In fact, MIDAS uses this for distributed applications using
a special ISAPI Dll that they have written. - httpsrvr.dll.
The DLL transfers the calls to the registered objects on the
server using DCOM. A big problem - the data format is proprietary.
Which says, pretty bluntly, that you can only use MIDAS clients
to access the server. What about the VB and VC++ developers
out there that want to use the service? We'll have to get
back to you on that.
SOAP is a protocol that defines a standard for passing data
across to the server. The headers that come to the server
are HTTP headers - but the body is XML. The XML is in a format
defined by SOAP - but the only real limitation is that you
need to enclose the XML in a certain set of tags and define
your namespace. Currently, SOAP can be implemented using ISAPI
Dlls on Windows NT, or through separate server programs.
If you were to communicate with a SOAP program, you would
need to format your XML and send it to the server - this is
something InternetExpress currently doesn't
allow you to extend its functionality to. With the architecture
I've outlined in this presentation, you can format your XML,
send it to a SOAP server, parse and integrate the results.
By the time this article is presented, there may be commercial
SOAP servers in place and there will be components to access
them.
Conclusion
Adopt XML in your applications and you'll see the benefits
in scalability. The technology has only recently become popular,
but you'll find a lot more progress in this field –
parsers, applications and servers that support XML. XML-Enable
your applications as soon as you can – you need to start
planning today.
References
1. Microsoft Windows DNA XML Resource Kit CD
2. Microsoft XML site (msdn.microsoft.com/xml)
3. Newsgroups:
- microsoft.public.xml @ msnews.microsoft.com
4. Mastering XML (Sybex) - Ann Navarro, Chuck White and Linda
Burman, ISBN 81-7656-191-6
5. The XML Handbook (Addison Wesley) - Charles F. Goldfarb,
Paul Prescod, ISBN 981-4035-87-4
Appendices
procedure
FormCreate(Sender: TObject);
begin
CoInitialize(nil );
// FDocument is a member variable of type IXMLDOMDocument
OleCheck(CoCreateInstance(Class_DOMDocument, nil,
CLSCTX_ALL,IXMLDOMDocument,
FDocument
end; |
Here's the code to load an XML Document and display it in
the List View:
procedure
TForm1.OpenFile(FileName : string);
begin
if FDocument.Load(FileName) then
ShowXML
else
ShowMessage('Could not load file : ' + FileName);
end;
procedure TForm1.ShowXML;
var
lstNodes : IXMLDOMNodeList;
node : IXMLDOMElement;
liStock : TListItem;
vVal : OleVariant;
szVal : string;
begin
//reset the list view
StockList.Items.BeginUpdate;
try
StockList.Items.Clear;
lstNodes := nil;
// select the nodes <quote> to </quote>
lstNodes := FDocument.selectNodes('//quote');
if (lstNodes <> nil)
then
while (true) do
begin
// traverse the nodes
node := lstNodes.nextNode as
IXMLDOMElement;
if node = nil
then // we're
done
break;
// we have a ticker, add a row
to the list view
liStock := StockList.Items.Add;
// now add each attribute to
the list view
liStock.Caption := node.getAttribute('symbol');
vVal := node.getAttribute('change');
szVal :='';
if VarIsEmpty(vVal)
or VarIsNull(vVal)
then
liStock.ImageIndex := 2 // no value
else
begin
szVal := vVal;
if szVal[1]
= '-' then // going down, show down
arrow bitmap
liStock.ImageIndex
:= 1
else
liStock.ImageIndex
:= 0;
end;
liStock.SubItems.Add(szVal);
vVal := node.getAttribute('price');
if not VarIsNull(vVal)
then liStock.SubItems.Add(vVal)
else liStock.SubItems.Add('');
vVal := node.getAttribute('open');
if not VarIsNull(vVal)
then liStock.SubItems.Add(vVal)
else liStock.SubItems.Add('');
vVal := node.getAttribute('volume');
if not VarIsNull(vVal)
then liStock.SubItems.Add(vVal)
else liStock.SubItems.Add('');
vVal := node.getAttribute('lastupdate');
if not VarIsNull(vVal)
then liStock.SubItems.Add(vVal)
else liStock.SubItems.Add('');
end;
finally
StockList.Items.EndUpdate;
end;
end;
|
Listing 2 - Stock Server
const
QUOTELINE = '<quote symbol="%s" price="%d"
change="%d" open="%d"
volume="%d" lastupdate="%s"/>';
function TForm1.BuildResultXML(szInputXML:
string): string;
var
doc : IXMLDOMDocument;
lstNodes : IXMLDOMNodeList;
node : IXMLDOMElement;
vVal : Variant;
begin
Result := '<quotes>';
doc := CoDomDocument.Create;
try
doc.loadXML( szInputXML );
lstNodes := doc.selectNodes('//quote');
if (lstNodes <> nil
) then
begin
while (true) do
begin
node := lstNodes.nextNode as
IXMLDOMElement;
if node = nil
then break;
vVal := node.getAttribute('symbol');
if not VarIsNull(vVal)
then
FResult := FResult + GetSymbolInfo(
vVal );
end;
FResult := Fresult + '</quotes>';
end;
finally
doc._Release;
end;
end;
function TForm1.GetSymbolInfo(szSymbol:
string): string;
begin
Result := '';
qrySymbol.Parameters.ParamByName('SYMBOL').Value
:= szSymbol;
qrySymbol.Open;
if not qrySymbol.IsEmpty then
Result := Format(QUOTELINE, [szSymbol,
Round(FieldbyName('PRICE').AsFloat),
Round(FieldByName('Change').AsFloat),
Round(FieldbyName('Open').AsFloat),
FieldbyName('Volume').AsInteger,
FormatDateTime('mm/dd/yyyy
hh:mm:ss',
FieldByname('LastUpdate').AsDateTime)]);
qrySymbol.Close;
end;
|
The server socket listens for a request, calls BuildXML and
sends the returned string back to the client.
Listing 3 - Data inserted into a client dataset
WebLink.Get('localhost/istest/webcust.dll');
szXML := WebLink.body;
OleCheck(CoCreateInstance(Class_DOMDocument, nil ,
CLSCTX_ALL,IXMLDOMDocument, Document));
Document.loadXML(szXML);
lstNodes := Document.selectNodes('//CUSTOMER');
if (lstNodes <> nil
) then
while (true) do
begin
node := lstNodes.nextNode as IXMLDOMElement;
if node = nil then
break;
cdsCustomer.Insert;
cdsCustomer.FieldByName('Src').AsInteger
:= 1;
szVal := node.getAttribute('CUSTNO');
cdsCustomer.FieldByName('CustNo').AsString
:= szVal;
compNode := node.selectSingleNode('COMPANY');
szVal := compNode.Text;
cdsCustomer.FieldByName('Company').AsString
:= szVal;
compNode := node.selectSingleNode('FNAME');
szVal := compNode.text;
compNode := node.selectSingleNode('LNAME');
szVal := szVal + ' ' + compNode.Text;
cdsCustomer.FieldByName('Contact').AsString
:= szVal;
compNode := node.selectSingleNode('STATE');
szVal := compNode.Text;
cdsCustomer.FieldByName('State').AsString
:= szVal;
compNode := node.selectSingleNode('COUNTRY');
szVal := compNode.Text;
cdsCustomer.FieldByName('Country').AsString
:= szVal;
cdsCustomer.Post;
end;
Document._Release; |
-------------------------------------------------------------------------------------------------------
|