Progress
External Program
Interfaces


Example 2 — Reading Customer Data and Writing a TEMP-TABLE

This example is a SAX version of the DOM example described in "XML Support," in this book. The example reads an XML file containing the Customer table from the Sports database and writes the data to a temp-table. The example uses qname, assumes there is no namespace prefix, and, for clarity, omits code for transaction scoping and for validation.

Here is the EPI SAX Example 2 driver procedure, e-sax2d.p.

e-sax2d.p
DEF VAR hParser as HANDLE. 
DEF VAR hHandler AS HANDLE. 
/* create the SAX-READER object */ 
CREATE SAX-READER hParser. 
/* run the persistent procedure that contains the callbacks */ 
RUN "e-sax2h.p" PERSISTENT SET hHandler. 
/* give the SAX-READER the handle to the persistent procedure */ 
hParser:HANDLER = hHandler. 
/* Give the SAX-READER the info on the file to parse. 
   This XML file does not use namespaces. */ 
hParser:SET-INPUT-SOURCE("FILE", "e-saxe2.xml"). 
hParser:SAX-PARSE( ) NO-ERROR. 
/* By the time SAX-PARSE returns, our callbacks have been called as many 
times as necessary and we’re done processing the XML document (or there 
was an error) */ 
IF ERROR-STATUS:ERROR THEN 
DO: 
  IF ERROR-STATUS:NUM-MESSAGES > 0 THEN 
    /* unable to begin the parse */ 
    MESSAGE ERROR-STATUS:GET-MESSAGE(1) VIEW-AS ALERT-BOX. 
  ELSE 
    /* error detected in a callback */ 
    MESSAGE RETURN-VALUE VIEW-AS ALERT-BOX. 
END. 
ELSE 
  MESSAGE "Document parsed successfully" VIEW-AS ALERT-BOX. 
DELETE OBJECT hParser. 
DELETE PROCEDURE hHandler. 

Here is the EPI SAX Example 2 XML file, e-sax2.xml.

e-sax2.xml 
<?xml version='1.0' ?> 
<Customers> 
 <Customer Name="Lift Line Skiing" Cust-num="1"> 
    <Country>USA</Country> 
    <Address>276 North Street</Address> 
    <Address2></Address2> 
    <City>Boston</City> 
    <State>MA</State> 
    <Postal-Code>02114</Postal-Code> 
    <Contact>Gloria Shepley</Contact> 
    <Phone>(617) 450-0087</Phone> 
    <Sales-Rep>HXM</Sales-Rep> 
    <Credit-Limit>66700</Credit-Limit> 
    <Balance>42568</Balance> 
    <Terms>Net30</Terms> 
    <Discount>35</Discount> 
    <Comments>This customer is on credit hold.</Comments> 
  </Customer> 
 <Customer Name="Hoops " Cust-num="3"> 
     <Country>USA</Country> 
     <Address>Suite 415</Address> 
     <Address2>40 Grove St.</Address2> 
     <City>Atlanta</City> 
     <State>GA</State> 
     <Postal-Code>02112</Postal-Code> 
     <Contact>Michael Traitser</Contact> 
     <Phone>(617) 355-1557</Phone> 
     <Sales-Rep>HXM</Sales-Rep> 
     <Credit-Limit>75000</Credit-Limit> 
     <Balance>1199.95</Balance> 
     <Terms>Net30</Terms> 
     <Discount>10</Discount> 
     <Comments>This customer is now OFF credit hold.</Comments> 
</Customer> 
</Customers> 

NOTE: There is no DTD and no use of namespace prefixes. The lack of a DTD means that the handlers need to validate the document, but the example omits that validation for the sake of clarity.

Here is the EPI SAX Example 2 handler procedure, e-sax2h.p (1 of 3).

e-sax2h.p
/*so we can create new records*/ 
DEFINE TEMP-TABLE Custt LIKE Customer. 
DEFINE VARIABLE hBuf AS HANDLE. 
DEFINE VARIABLE hDBFld AS HANDLE. 
hBuf = BUFFER Custt:HANDLE. 
/* variable in which to accumulate all the text data for one element 
coming in through potentially multiple calls (per element) to the 
Characters procedure */ 
DEF VAR currentFieldValue as CHAR. 
/* simple-minded state machine – the code makes minimal use of it, 
but it could easily be used to validate the structure of the document 
in this example */ 
DEF VAR iProcessingState as INT. 
&SCOPED-DEFINE READY-TO-START 1 
&SCOPED-DEFINE GETTING-RECORDS 2 
&SCOPED-DEFINE GETTING-FIELDS 3 
&SCOPED-DEFINE DONE 4 
PROCEDURE StartDocument: 
  iProcessingState = {&READY-TO-START}. 
END. 
PROCEDURE StartElement: 
  DEFINE INPUT PARAMETER namespaceURI AS CHARACTER. 
  DEFINE INPUT PARAMETER localName AS CHARACTER. 
  DEFINE INPUT PARAMETER qName AS CHARACTER. 
  DEFINE INPUT PARAMETER attributes AS HANDLE. 
  IF qName = "Customers" THEN 
    iProcessingState = {&GETTING-RECORDS}. 
  ELSE IF qName = "Customer" THEN 
  DO: 
    /* starting a new customer, so create the record */ 
    CREATE Custt. 
     
    /*get the fields that are in the XML doc as attributes*/ 
    cust-num = INTEGER( attributes:GET-VALUE-BY-QNAME("Cust-num") ). 
    name = attributes:GET-VALUE-BY-QNAME( "Name" ). 
    iProcessingState = {&GETTING-FIELDS}. 
  END. 
  ELSE IF iProcessingState = {&GETTING-FIELDS} THEN 
  DO: 
    /* get a handle to the field whose name corresponds 
       to the element name */ 
    hDBFld = hBuf:BUFFER-FIELD(qName). 
     
    /* re-init the variable in which we accumulate the field value */ 
    currentFieldValue = "". 
  END. 
END. 
PROCEDURE Characters: 
  DEFINE INPUT PARAMETER charData AS MEMPTR. 
  DEFINE INPUT PARAMETER numChars AS INTEGER. 
   
  /* get the text value of the field (hDBFld was set to the correct field 
     in StartElement */ 
  currentFieldValue =  
      currentFieldValue + GET-STRING(charData, 1, GET-SIZE(charData)). 
END. 
PROCEDURE EndElement: 
  DEFINE INPUT PARAMETER namespaceURI AS CHARACTER. 
  DEFINE INPUT PARAMETER localName AS CHARACTER. 
  DEFINE INPUT PARAMETER qName as CHARACTER. 
  IF localName = "Customers" THEN 
    iProcessingState = {&DONE}. 
  ELSE IF localName = "Customer"THEN 
    iProcessingState = {&GETTING-RECORDS}. 
  ELSE IF iProcessingState = {&GETTING-FIELDS} THEN 
    hDBFld:BUFFER-VALUE = currentFieldValue. 
END. 
PROCEDURE EndDocument: 
  /* show that data made it by displaying temp-table */ 
  FOR EACH Custt: 
    DISPLAY custt. 
  END. 
  RUN Cleanup. 
END. 
PROCEDURE FatalError: 
  DEFINE INPUT PARAMETER errMessage AS CHARACTER. 
  /* not necessary to do anything with PRIVATE-DATA, this is 
     just an example of what you could do */ 
  SELF:PRIVATE-DATA = "FATAL". 
  RUN Cleanup. 
   
  /* RETURN ERROR in an error handler implicitly calls SELF:STOP-PARSING(), 
     sets SELF:PARSE-STATUS to SAX-PARSER-ERROR, and raises the Progress 
     ERROR condition */ 
  RETURN ERROR errMessage 
            + "(Line " + CHR(SELF:LOCATOR-LINE-NUMBER) 
            + ", Col " + CHR(SELF:LOCATOR-COLUMN-NUMBER) 
            + ")". 
END. 
/* this is not a SAX callback, it is just a local utility */ 
PROCEDURE Cleanup: 
  /* in case we've parsed previous documents */ 
  hBuf:EMPTY-TEMP-TABLE( ). 
END. 


Copyright © 2004 Progress Software Corporation
www.progress.com
Voice: (781) 280-4000
Fax: (781) 280-4095