modula-2 home

  Home  
  Tutorial  
  Win32 API  
  Reference  
  Projects  
 

 

Reading XML files

For ISO Module-2

Tested with XDS and Stony Brook Modula-2

By Frank Schoonjans (frank.schoonjans@medcalc.net) - 7 December 2004

 

I am working on a library to read XML files. When programming the parser I wanted to avoid (a) the use of recursion, and (b) any limitations on string length.

Error reporting is limited.

I have tested with Stony Brook and XDS. In XDS, you must select the Project option "Enable language extensions" and "Default memory management".

I hope to receive your comments or suggestions.

The XMLRead library has the following interface:

DEFINITION MODULE XMLReadLib;

FROM SYSTEM IMPORT CAST;

TYPE StringPointer = POINTER TO ARRAY OF CHAR;

TYPE XMLHandle;

CONST INVALID_XMLHandle=CAST(XMLHandle,NIL);

PROCEDURE XMLOpenDocument(filename : ARRAY OF CHAR) : XMLHandle;
PROCEDURE XMLCloseDocument(VAR handle: XMLHandle);

TYPE AttrPair   = RECORD
                    IdPtr  : StringPointer;
                    ValPtr : StringPointer;
                  END;

     PairRecPtr = POINTER TO ARRAY OF AttrPair;

     XMLElementRec = RECORD
                       NamePtr    : StringPointer;
                       DataPtr    : StringPointer;
                       AttribSz   : CARDINAL; (* number of Attributes *)
                       Attributes : PairRecPtr;
                     END;

     Tags = (opentag,closetag,emptyelementtag,processinginstruction,comment);

TYPE XMLCallbackProc = PROCEDURE(CARDINAL, Tags, XMLElementRec) : BOOLEAN;

PROCEDURE XMLSetCallback(handle   : XMLHandle;
                         userdata : CARDINAL;
                         proc     : XMLCallbackProc) : BOOLEAN;

PROCEDURE XMLParseDocument(handle : XMLHandle) : INTEGER;
(* returns  -1 = Invalid file handle
             0 = OK
             1 = Found '<' but expected '>'
             2 = Found '>' but expected '<'
             3 = Wrong end tag
             4 = Not all elements are closed
             5 = File contains trailing text
             6 = Found '>' but expected '?>'
             7 = Found '<!' but expected '<!--'
             8 = Found '>' but expected '-->'
             9 = Expected closing "
            10 = Expected =
            15 = Out of memory
            *)

PROCEDURE XMLGetErrorString(handle : XMLHandle; VAR strptr : StringPointer);

END XMLReadLib.

Here is a simple test program

MODULE ReadTest;

FROM XMLReadLib IMPORT XMLHandle,XMLOpenDocument,XMLSetCallback,XMLParseDocument,
                       XMLCloseDocument,XMLElementRec,Tags,XMLGetErrorString,
                       StringPointer;

FROM STextIO    IMPORT WriteString,WriteLn,ReadChar;

PROCEDURE ShowElement(element : XMLElementRec);
VAR c : CARDINAL;
BEGIN
    WriteString(element.NamePtr^); WriteString("'"); WriteLn;
    IF element.DataPtr#NIL THEN
       WriteString("       data : '");
       WriteString(element.DataPtr^);
       WriteString("'");
       WriteLn;
    END;
    IF element.AttribSz>0 THEN
       FOR c:=0 TO element.AttribSz-1 DO
          WriteString("              '");
          WriteString(element.Attributes^[c].IdPtr^);
          WriteString("' : '");
          WriteString(element.Attributes^[c].ValPtr^);
          WriteString("'");
          WriteLn;
       END;
    END;
END ShowElement;

PROCEDURE CallbackFunction(userdata : CARDINAL;
                           tag      : Tags;
                           element  : XMLElementRec) : BOOLEAN;
BEGIN
    CASE tag OF
    | processinginstruction : WriteString("Proc instr. : '"); ShowElement(element);
    | opentag               : WriteString("Open tag    : '"); ShowElement(element);
    | closetag              : WriteString("Close tag   : '"); ShowElement(element);
    | emptyelementtag       : WriteString("Empty elem. : '"); ShowElement(element);
    | comment               : WriteString("Comment     : '"); ShowElement(element);
    END;
    RETURN TRUE;
END CallbackFunction;

VAR handle : XMLHandle;
    ch     : CHAR;
    str    : StringPointer;
BEGIN
    handle:=XMLOpenDocument("test.xml");
    IF XMLSetCallback(handle,1,CallbackFunction) THEN END;
    IF XMLParseDocument(handle)#0 THEN
         WriteString("ERROR : ");
         XMLGetErrorString(handle,str);
         WriteString(str^);
         WriteLn;
    END;
    XMLCloseDocument(handle);
    ReadChar(ch);
END ReadTest.

After you open the file using XMLOpenDocument, you assign a callback procedure to it with XMLSetCallback.

Next call XMLParseDocument and finally close the file using XMLCloseDocument.

The XMLParseDocument procedure will call the Callback function you have defined when it encounters a processing instruction (enclosed in <?  ?>), an open tag (enclosed in <  >), a close tag (enclosed in </  >), an empty-element-tag (enclosed in <  />) or a comment (enclosed in <!--  -->).

  • The first parameter of the callback producure is the CARDINAL number (of your choice) that you have specified in the XMLSetCallback function.
  • The second parameter of the callback producure is the type of tag found in the XML file (opentag, closetag, emptyelementtag, processinginstruction, comment).
  • The third parameter is a record with tag or element information. E.g. for the element
    <document author="frank">
    data
    </document>
    

    The members of the XMLElementRec record are:

    NamePtr : a pointer to the string "document"
    DataPtr : a pointer to the string "data"
    AttribSz : 1, and
    Attributes[0].NamePtr : a pointer to the string "author"
    Attributes[0].ValuePtr : a pointer to the string "frank"

For the following XML file:

<?xml version="1.0"?>
<document author="frank">
     <empty_element/>
     <field1>
          <!--comment 123-->data for field 1
          <sub1>sub stuff 1</sub1>
          <sub2>sub stuff 2</sub2>
     </field1>
     <field2>data for field2</field2>
</document>

the output of our test program is:

Proc instr. : 'xml'
              'version' : '1.0'
Open tag    : 'document'
              'author' : 'frank'
Empty elem. : 'empty_element'
Open tag    : 'field1'
Comment     : 'comment 123'
Open tag    : 'sub1'
Close tag   : 'sub1'
       data : 'sub stuff 1'
Open tag    : 'sub2'
Close tag   : 'sub2'
       data : 'sub stuff 2'
Close tag   : 'field1'
       data : 'data for field 1'
Open tag    : 'field2'
Close tag   : 'field2'
       data : 'data for field2'
Close tag   : 'document'
              'author' : 'frank'

Download

XMLReadLib.rar