Progress
External Program
Interfaces


Example 1 — Retrieving Names and Phone Numbers

This example retrieves names and phone numbers. It is presented in two versions in the following sections:

Without Namespace Processing

Here is the EPI SAX Example 1 driver, e-sax1d.p (1 of 2). The driver’s logic closely parallels the tasks in the "Basic Tasks Of a Progress SAX Application" section.

e-sax1d.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-sax1h.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-saxe1.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 1 XML file, e-sax1.xml. Each entry contains a name and phone number.

e-sax1.xml 
<?xml version='1.0' ?> 
<Phonelist> 
  <Entry ContactName="Jane Jones"> 555 555-5555 </Entry> 
  <Entry ContactName="John Smith"> 555 555-1111 </Entry> 
</Phonelist> 

Here is the EPI SAX Example 1 handler procedure, e-sax1h.p (1 of 2). It contains the callbacks.

e-sax1h.p
/* This small example uses a very simple approach to keeping track of where 
it is in the processing of the document. It uses currentPerson and 
currentNum, which are variables global to this procedure that enable the 
application to tie together the several different pieces of information 
that it gets for each element in the XML document. */ 
/* Name attribute for the current entry. App gets it during the StartElement 
callback */ 
DEF VAR currentPerson AS CHARACTER. 
/* Phone number from the current entry. App gets it during the Characters 
callback because it is the character data for the element. */ 
DEF VAR currentNum AS CHARACTER. 
/* This procedure is called when the parser finds the start tag for an element. 
For this particular XML doc, the app simply looks for “Entry” elements and 
digs out the “ContactName” attribute during the StartElement call, saving 
it in currentPerson */ 
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 = "Entry" THEN 
    currentPerson = attributes:GET-VALUE-BY-QNAME("ContactName"). 
END. 
/* This callback gets passed the character data for an element. Note that SAX 
does not guarantee that all the characters for an element get passed in 
one call -- that's why the app has to maintain the currentNum global 
variable and append to it when handling Characters, and also why it has to 
wait for EndElement before displaying the message box. (Note also that some 
apps may need to use a MEMPTR to accumulate the character data, which may 
exceed the 32K Progress CHARACTER variable limit) */ 
PROCEDURE Characters: 
  DEFINE INPUT PARAMETER charData AS MEMPTR. 
  DEFINE INPUT PARAMETER numChars AS INTEGER. 
  /* ( Can assume that any call to Characters is for an Entry's text value, 
    because we know what the document looks like. If this weren't the case, 
    we'd have to keep track of the localName passed to the most recent call to 
    StartElement) */ 
  currentNum = currentNum + GET-STRING(charData, 1, GET-SIZE(charData)). 
END. 
/* This callback is called when the parser finds the end tag for an Element. 
Note that this app only cares about the end of an Entry element.*/ 
PROCEDURE EndElement: 
  DEFINE INPUT PARAMETER namespaceURI AS CHARACTER. 
  DEFINE INPUT PARAMETER localName AS CHARACTER. 
  DEFINE INPUT PARAMETER qName AS CHARACTER. 
  IF qName = "Entry" THEN 
  DO: 
    MESSAGE "Name: " currentPerson SKIP  
            "Phone Number: " currentNum VIEW-AS ALERT-BOX. 
    currentNum = "". 
    currentPerson = "". 
  END. 
END. 
/* note that, knowing the structure of the XML doc, the app could have done 
this in the EndElement call for the Phonelist element and could then have 
omitted EndDocument altogether. */ 
PROCEDURE EndDocument: 
  MESSAGE "All Done" VIEW-AS ALERT-BOX. 
END. 

When Example 1 is run, it produces the trace shown in Table 12–8.

Table 12–8: Trace of Example 1
Callback function: StartDocument 
Callback function: StartElement 
  namespaceURI:   
  localName: Phonelist 
  qName: Phonelist 
  SAX-ATTRIBUTE has 0 items: 
Callback function: StartElement 
  namespaceURI:   
  localName: Entry 
  qName: Entry 
  SAX-ATTRIBUTE has 1 items: 
    Attribute 1 :  
      namespaceURI:   
      localName: ContactName  
      qName: ContactName  
      type: CDATA  
      value: Jane Jones 
Callback function: Characters 
  charData:  555 555-5555 
Callback function: EndElement 
  namespaceURI:   
  localName: Entry  
  qName: Entry 
Callback function: StartElement 
  namespaceURI:   
  localName: Entry  
  qName: Entry 
  SAX-ATTRIBUTE has 1 items: 
    Attribute 1 :  
      namespaceURI:   
      localName: ContactName  
      qName: ContactName  
      type: CDATA  
      value: John Smith 
Callback function: Characters 
  charData:  555 555-1111 
Callback function: EndElement 
  namespaceURI:   
  localName: Entry  
  qName: Entry 
Callback function: EndElement 
  namespaceURI:   
  localName: Phonelist  
  qName: Phonelist 
Callback function: EndDocument 

With Namespace Processing

Here is the same example except that the XML document uses Namespaces. Consequently, the StartElement and EndElement callbacks in the handler use the namespaceURI and localName parameters rather than qName.

NOTE: The original example could have used localName by itself, but did not.

Here is the EPI SAX Example 1 driver with namespace processing, e-sax1dn.p (1 of 2).

e-sax1dn.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-saxe1h-ns.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 uses namespaces. */ 
hParser:SET-INPUT-SOURCE("FILE", "e-saxe1-ns.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 1 XML file with namespace processing, e-sax1n.xml.

e-sax1n.xml 
<?xml version='1.0' ?> 
<Phonelist xmlns="http:/www.wmhwmh.biz/ns/Default" 
           xmlns:pl="http://www.wmhwmh.biz/ns/phonelist"> 
  <pl:Entry pl:ContactName="Jane Jones"> 555 555-5555 </pl:Entry> 
  <pl:Entry pl:ContactName="John Smith"> 555 555-1111 </pl:Entry> 
</Phonelist> 

Here is the EPI SAX Example 1 handler procedure with namespace processing, e-sax1hn.p (1 of 3).

e-sax1hn.p
/* Name attribute for the current entry. App gets it during the StartElement 
callback */ 
DEF VAR currentPerson AS CHARACTER. 
/* Phone number from the current entry. App gets it during the Characters 
callback because it is the character data for the element. */ 
DEF VAR currentNum AS CHARACTER. 
/* This procedure is called when the parser finds the start tag for an element. 
  For this particular XML doc, the app simply looks for "Entry" elements and 
  digs out the "ContactName" attribute during the StartElement call, saving it 
  in currentPerson.  
  The code assumes that Namespace processing is enabled and checks 
  to make sure that name parameters are part of the correct namespace  
  */ 
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 namespaceURI = "http://www.wmhwmh.biz/ns/phonelist" THEN 
  DO: 
     IF localName = "Entry" THEN 
        currentPerson = attributes:GET-VALUE-BY-NAMESPACE-NAME( 
                                       "http://www.wmhwmh.biz/ns/phonelist", 
                                       "ContactName" ). 
  END. 
END. 
/* This callback gets passed the character data for an element. SAX 
does not guarantee that all the characters for an element get passed in 
one call -- that's why the app has to maintain the currentNum global 
variable and append to it when handling Characters, and also why it has to 
wait for EndElement before displaying the message box. (Some apps may 
need to use a MEMPTR to accumulate the character data, which may 
exceed the 32K Progress CHARACTER variable limit) */ 
PROCEDURE Characters: 
  DEFINE INPUT PARAMETER charData AS MEMPTR. 
  DEFINE INPUT PARAMETER numChars AS INTEGER. 
  /* ( Can assume that any call to Characters is for an Entry's text value, 
    because we know what the document looks like. If this weren't the case, 
    we'd have to keep track of the localName passed to the most recent call to 
    StartElement) */ 
  currentNum = currentNum + GET-STRING(charData, 1, GET-SIZE(charData)). 
END. 
/* This callback is called when the parser finds the end tag for an Element. 
This app only cares about the end of an Entry element.*/ 
PROCEDURE EndElement: 
  DEFINE INPUT PARAMETER namespaceURI AS CHARACTER. 
  DEFINE INPUT PARAMETER localName AS CHARACTER. 
  DEFINE INPUT PARAMETER qName AS CHARACTER. 
  IF namespaceURI = "http://www.wmhwmh.biz/ns/phonelist" THEN 
  DO: 
     IF  localName = "Entry" THEN 
     DO: 
       MESSAGE "Name: " currentPerson SKIP  
            "Phone Number: " currentNum VIEW-AS ALERT-BOX. 
       currentNum = "". 
       currentPerson = "". 
     END. 
  END. 
END. 
/* note that, knowing the structure of the XML doc, the app could have done 
this in the EndElement call for the Phonelist element and could then have 
omitted EndDocument altogether. */ 
PROCEDURE EndDocument: 
  MESSAGE "All Done" VIEW-AS ALERT-BOX. 
END. 

When Example 1 with namespace processing is run, it produces the trace shown in Table 12–9.

Table 12–9: Trace of Example 1 With Namespace Processing
Callback function: StartDocument 
Callback function: StartElement 
  namespaceURI: http:/www.wmhwmh.biz/ns/Default  
  localName: Phonelist  
  qName: Phonelist 
  SAX-ATTRIBUTE has 0 items: 
Callback function: StartElement 
  namespaceURI: http://www.wmhwmh.biz/ns/phonelist  
  localName: Entry  
  qName: pl:Entry 
  SAX-ATTRIBUTE has 1 items: 
    Attribute 1 :  
      namespaceURI: http://www.wmhwmh.biz/ns/phonelist  
      localName: ContactName  
      qName: pl:ContactName  
      type: CDATA  
      value: Jane Jones 
Callback function: Characters 
  charData:  555 555-5555 
Callback function: EndElement 
  namespaceURI: http://www.wmhwmh.biz/ns/phonelist  
  localName: Entry  
  qName: pl:Entry 
Callback function: StartElement 
  namespaceURI: http://www.wmhwmh.biz/ns/phonelist  
  localName: Entry  
  qName: pl:Entry 
  SAX-ATTRIBUTE has 1 items: 
    Attribute 1 :  
      namespaceURI: http://www.wmhwmh.biz/ns/phonelist  
      localName: ContactName  
      qName: pl:ContactName  
      type: CDATA  
      value: John Smith 
Callback function: Characters 
  charData:  555 555-1111 
Callback function: EndElement 
  namespaceURI: http://www.wmhwmh.biz/ns/phonelist  
  localName: Entry  
  qName: pl:Entry 
Callback function: EndElement 
  namespaceURI: http:/www.wmhwmh.biz/ns/Default  
  localName: Phonelist  
  qName: Phonelist 
Callback function: EndDocument 


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