*************************************
* *
* DB/C Newsletter *
* November 2001 *
* *
*************************************
News and Comments
The beta testing of DB/C DX 12 is progressing well. Unless we run into
some unforeseen issues, DB/C DX 12 will be released in December.
XML continues to grow in popularity. It is being used in a variety of
places, most somehow related to ecommerce. We've discussed XML several times
here, including the short tutorial in the September 1999 DB/C Newsletter.
Several DB/C users have started using XML in one way or another. The
first thing that most encounter is the need for a set of routines to build
and traverse XML data. This month's newsletter describes a DB/C class that
provides such features.
I'm the author of that DB/C XML program. It's been a while since I've
written any DB/C programs, so I know the code isn't particularly pretty.
I'm sure the code contains bugs, it doesn't handle error situations, and it
needs an import parser to verify the XML validity. If you fix any bugs or
make any enhancements, we would appreciate it if you would send us those
changes so we can incorporate them in the source files that we provide in
the Sample Code place at www.dbcsoftware.com.
Here's a useful tip. While I was testing the code for this month's
newsletter, I noticed that each time I started the runtime (dbcc.exe or
dbc.exe) there was a delay of a few seconds. This delay is really annoying.
If you are testing in an environment where you have exclusive use of the
files, you can make the delay disappear by specifying dbcdx.file.sharing=off
in the dbcdx.cfg file.
don.wills@dbcsoftware.com
******************************************************************************
DB/C and XML
At its most basic level, XML is a very simple method of representing
hierarchical data structures. There are essentially three concepts in an
XML data structure: elements, attributes and text. Elements are either
standalone, or they contain sub-elements and text. In addition, one or
more uniquely named attributes are associated with an element. Here are
some text representations of an element:
The first example above is an element that contains only the element name
(or tag). It contains no sub-elements or attributes. The date="15NOV2001"
field is an attribute that is associated with the element. The second and
third examples are logically the same. When the '/' is at the end of an
element, it signifies that there are no sub-elements. When the '/' is
before the element name, it signifies the end of the sub-elements of this
element.
Here is an example XML element from the September 1999 DB/C Newsletter:
A56223
1999-09-30
ABC Video
100 Main Street
Ames
Iowa
55555
Bill Riley
Acme Supply
POBox 123
15 Broadway
Chicago
IL
66666
Joe
-
5
431321-2
Titanic (DVD)
-
10
744311-0
Star Wars: Episode 1 (VHS)
Programmers need to be able to read and write XML data. Generally,
the XML data is stored in files. Some XML files contain just a single
element (like the example above), whereas other XML files contain multiple
separate XML elements. When a file will contain multiple elements, it is
usually easier just to deal with the XML elements one at a time. Thus, the
two basic needs for a programmer to deal with XML are:
1. to export an XML element from the program to an output stream or file
2. to import an XML element into the program from in input stream or file
When creating a set of routines to deal with the XML data while it is
inside the program, the most important design issue is this: How are
elements, attributes and text represented?
In programming languages like C++ and Java, it is simple and obvious -
each element, attribute and text chunk is an object.
In DB/C it is not that simple. In the DB/C language, a CLASS is a
'heavy' entity. That is, a lot of baggage goes along with each OBJECT
that is instantiated from a CLASS. (The reason for this is that a CLASS
is essentially equivalent to a LOADMOD (.DBC) which is large and contains
a lot of state information.) Thus, when many hundreds or thousands of
OBJECTS exist in the DB/C runtime, performance will be unacceptable.
As an XML element can contain hundreds of sub-elements, attributes and
text chunks, a different representation is necessary for representing an XML
element in DB/C. The design I've chosen for the XML code is that a single
OBJECT represents an XML element along with its attributes, sub-elements
and text chunks. Associated with this OBJECT is set of positions that are
used to traverse through the attributes and elements. These positions are
conceptually similar to the file position information associated with FILE,
IFILE and AFILE variables.
Inside the XML OBJECT, I chosen to represent the data in exactly the
same format in which it is imported and exported - a string of characters.
The code assumes that the element will fit into a single DIM variable, thus
the maximum number of characters in an element is limited by the maximum
size of a DIM variable which is currently 65500 characters.
The DB/C XML code is contained in an CLASS DEFINITION for the XMLCODE
class. The XMLCODE.PRG file contains this CLASS DEFINITION. The routines
in this program are enclosed in several VERBS. The XML.DEF file contains
these the VERB definitions. The source files can be found at the Sample Code
place at www.dbcsoftware.com.
Instead of trying to describe the routines in XMLCODE.PRG, it's easier
to provide examples. The XML.DEF file follows the examples.
Here is a sample program that uses the XMLCODE CLASS to create the
PurchaseOrder XML element above:
INC XML.DEF
PO OBJECT
OUT CHAR 8000
XMLMAKE PO, "PurchaseOrder" . create PO object with node name
XMLADDNODE PO, "OrderNumber", SETPOS . new sub-element and set position
XMLADDTEXT PO, "A56223" . add text below OrderNumber
XMLSETPARENT PO . set position to PurchaseOrder
XMLADDNODE PO, "OrderDate", SETPOS . new sub-element and set position
XMLADDATTR PO, "datetype", "ISO" . add attribute to OrderDate
XMLADDTEXT PO, "1999-09-30" . add text below OrderDate
XMLSETPARENT PO . set position to PurchaseOrder
XMLADDNODE PO, "Nontaxable" . add sub-element
XMLADDNODE PO, "Purchaser", SETPOS . add sub-element and set position
XMLADDNODE PO, "Name", TEXT="ABC Video" . add sub-element and text below
XMLADDNODE PO, "Street", TEXT="100 Main Street"
XMLADDNODE PO, "City", TEXT="Ames"
XMLADDNODE PO, "State", TEXT="Iowa"
XMLADDNODE PO, "PostalCode", TEXT="55555"
XMLADDNODE PO, "Contact", TEXT="Bill Riley"
XMLSETPARENT PO . set position up to PurchaseOrder
XMLADDNODE PO, "Vendor", SETPOS
XMLADDNODE PO, "Name", TEXT="Acme Supply"
XMLADDNODE PO, "Street", TEXT="POBox 123"
XMLADDNODE PO, "Street", TEXT="15 Broadway"
XMLADDNODE PO, "City", TEXT="Chicago"
XMLADDNODE PO, "State", TEXT="IL"
XMLADDNODE PO, "PostalCode", TEXT="66666"
XMLADDNODE PO, "Contact", TEXT="Joe"
XMLSETPARENT PO
XMLADDNODE PO, "Details", SETPOS
XMLADDNODE PO, "Item", SETPOS
XMLADDNODE PO, "Quantity", TEXT="5"
XMLADDNODE PO, "Number", TEXT="431321-2"
XMLADDNODE PO, "Desc", TEXT="Titanic (DVD)"
XMLSETPARENT PO
XMLADDNODE PO, "Item", SETPOS
XMLADDNODE PO, "Quantity", TEXT="10"
XMLADDNODE PO, "Number", TEXT="744311-0"
XMLADDNODE PO, "Desc", TEXT="Star Wars: Episode 1"
XMLEXPORT PO, OUT . move XML from PO to OUT
FILE FILE VAR=8000, TEXT
SEQ FORM "-1"
PREP FILE, "XMLOUT.TXT"
WRITE FILE, SEQ; *+, OUT . for example, write OUT
STOP
Here is a sample program that uses the XMLCODE CLASS to read and print
the PurchaseOrder XML element above:
INC XML.DEF
PO OBJECT
IN CHAR 8000
C1 CHAR 40
C2 CHAR 40
C3 CHAR 40
SEQ FORM "-1"
FILE FILE VAR=8000, TEXT
OPEN FILE, "XMLOUT"
READ FILE, SEQ; IN . for example, read IN
XMLMAKE PO . create empty PO
XMLIMPORT PO, IN . import the XML data into PO
XMLGETNAME PO, C1 . the tag name of the top element
GOTO ERROR IF (C1 != "PurchaseOrder") . should be PurchaseOrder
DISPLAY "Start of Purchase Order"
LOOP . loop through each sub-element
XMLGETNEXT PO, C1, SETPOS . C1 is name, set pos down 1 level
BREAK IF OVER . no more sub-elements
CONTINUE IF LESS . ignore any text at this level
SWITCH C1
CASE "OrderNumber"
XMLGETNEXT PO, C2 . assume the sub-element is text
DISPLAY "Order number: ", *LL, C2
CASE "OrderDate"
DISPLAY "Order date: ";
XMLGETATTR PO, C2, C3 . get the attribute name and value
IF NOT OVER . OVER means no attributes
DISPLAY "(", *LL, C2, "=#"", *LL, C3, "#") ";
ENDIF
XMLGETNEXT PO, C2 . assume the sub-element is text
DISPLAY *LL, C2
CASE "Purchaser"
DISPLAY "Customer:"
CALL DISPLAYNAMEADDR . print name and address
CASE "Vendor"
DISPLAY "Vendor:"
CALL DISPLAYNAMEADDR . print name and address
CASE "Details"
CALL DETAILS
ENDSWITCH . ignore any unknown sub-elements
XMLSETPARENT PO . set position up 1 level
REPEAT
DISPLAY "End of Purchase Order"
PAUSE "2"
STOP
DISPLAYNAMEADDR
LOOP
XMLGETNEXT PO, C2, SETPOS . C2 is name, set pos down 1 level
RETURN IF OVER . no more sub-elements
CONTINUE IF LESS . ignore any text at this level
XMLGETNEXT PO, C3 . C3 contains the text value
CONTINUE IF NOT LESS . should not occur (sub-element)
DISPLAY " ", *LL, C2, ": ", *LL, C3
XMLSETPARENT PO . set position up 1 level
REPEAT
DETAILS
DISPLAY "" . NOT IMPLEMENTED
RETURN
ERROR DISPLAY "ERROR!"
PAUSE "2"
STOP
Here is the XML.DEF file which includes the documentation for the
CVERBS in the XMLCODE class.
.
. definitions for using xmlcode class
.
xmlcode class module="xmlcode"
xmlmake verb !make(xmlcode), =cvarlit, maxchars=nvarlit
xmlfree verb !destroy
xmlimport verb !method, #cvarlit
xmlexport verb !method, #cvar
xmlgetname verb !method, #cvar
xmlgetattr verb !method, #cvar, #cvar
xmlgetnext verb !method, #cvar, setpos
xmladdnode verb !method, #cvarlit, setpos, text=cvarlit
xmladdtext verb !method, #cvarlit
xmladdattr verb !method, #cvarlit, #cvarlit
xmlgetpos verb !method, #cvar
xmlsetpos verb !method, #cvar
xmlsetparent verb !method
xmlsetchild verb !method
xmlreset verb !method
.
. XML import, export and usage routines
.
. The object created by xmlmake defines a single XML element along
. with all of its sub-elements and text. Three traversal positions are
. associated with this object. The value of the current node traversal
. position defines the current element (node) of the XML tree. The
. value of the attribute traversal position defines the position of the
. attribute returned by XMLGETATTR. The sub-element traversal position
. defines the position of the sub-element (node or text) that is returned
. by XMLGETNEXT. The current node traversal position is set by XMLSETPOS,
. XMLSETPARENT, XMLSETCHILD and by the XMLGETNEXT verb if the SETPOS operand
. is specified. XMLRESET resets the attribute and sub-element traversal
. positions without altering the current node traversal position.
.
. Syntax and description (optional operands in square brackets):
.
. XMLMAKE [cvar,] [MAXCHARS=nvarlit]
. Create an XML object. If the cvar operand is specified, the XML
. object will have one element with tagname specified by cvar. If
. cvar is not specified, an empty XML object is created. The maximum
. number of characters that this XML object can contain is specified
. by nvarlit. Default is 8000.
.
. XMLFREE
. Free the resources associated with this XML object.
.
. XMLIMPORT cvarlit
. Populate this XML object with nodes and text by parsing the stream
. of characters contained in the required operand.
.
. XMLEXPORT cvar
. Create the XML stream of characters from this XML object.
.
. XMLGETNAME cvar
. Move the tag name of the current node into the destination operand.
.
. XMLGETATTR cvar, cvar
. Move the attribute traversal position to the next attribute and
. move the attribute name to the first destination operand and the
. value of the attribute to the second operand. Set OVER flag and
. clear the operands if there are no more attributes.
.
. XMLGETNEXT cvar, [SETPOS]
. Move the sub-element traversal position to the next sub-element.
. If the next sub-element is text, the LESS flag is set and the
. text is moved to the destination operand. If the next sub-element
. is a node, the LESS flag is cleared and the name of the node is
. moved to the destination operand. In addition, if the LESS flag
. is cleared and SETPOS is specified, the current node traversal
. position is set so that the sub-element becomes the current node,
. and the attribute and sub-element traversal positions are reset.
.
. XMLADDTEXT cvar
. Add text after the last sub-element of the current element.
.
. XMLADDNODE cvar, [cvar,] [SETPOS]
. Add an new sub-element after the last sub-element of the current
. element. The tag name of the new element is specified by the
. operand. The current sub-element traversal position is reset.
. If the second cvar operand is specified, then a text sub-element
. is added below the new sub-element. If the SETPOS operand is
. specified, then an operation equivalent to XMLSETCHILD also
. occurs. That is, if SETPOS is specified, the current node is
. set to the sub-element just added.
.
. XMLADDATTR cvar, cvar
. Add an attribute to the current node. The first operand is the
. name of the attribute and the second operand is the value of the
. attribute. The attribute and sub-element traversal positions are
. reset.
.
. XMLSETCHILD
. If the current sub-element traversal position is invalid (reset),
. then the OVER flag is set and nothing else occurs. Otherwise,
. the OVER flag is cleared and the three traversal positions are
. pushed onto the traversal position stack. Then the current node
. traversal position is set to the sub-element traversal position,
. the attribute and sub-element traversal positions are reset.
.
. XMLSETPARENT
. If the traversal position stack is empty, the OVER flag is set
. and nothing else occurs. Otherwise, clear the OVER flag and pop
. the three traversal positions from the traversal position stack.
.
. XMLGETPOS cvar
. Move a string of characters that is the three traversal positions
. to the destination operand. The destination variable must be at
. least 15 characters long. The XMLGETPOS and XMLSETPOS verbs
. are only useful if the value of the XML element does not change
. between the execution of XMLGETPOS and execution of XMLSETPOS.
.
. XMLSETPOS cvar
. Set the three traversal position to the values specified by the
. operand which was set by the XMLGETPOS verb. The XMLSETPOS verb
. does not operate correctly if the value of the XML element changes
. between the execution of XMLGETPOS and XMLSETPOS.
.
. XMLRESET
. Reset the current attribute and sub-element traversal positions
. to the logical position before the first sub-element and attribute.
.
******************************************************************************
DB/C Class Schedule
Class: DB/C DX and JX Language Fundamentals
Date: January 21-23, 2002
Location: Oak Brook, Illinois
For information, send email to admin@dbcsoftware.com.
******************************************************************************
Subscribing to the DB/C Newsletter
If you don't already have the DB/C Newsletter delivered to your email
address and would like to have it emailed to you monthly, just send an
email message to 'dbcnews-subscribe@dbcsoftware.com'. The newsletter will
be delivered to the email address from which the message was sent.