Release 10.1A: OpenEdge Development:
Programming 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

> - With namespace processing

> ##### Without namespace processing

> Here is the EPI SAX Example 1 driver, i-sax1d.p. The driver’s logic closely parallels the tasks in the "Basic tasks of a Progress SAX application" section:

> i-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 "i-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", "i-sax1.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, i-sax1.xml. Each entry contains a name and phone number:

> i-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, i-sax1h.p (1 of 2). It contains the callbacks:

> i-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 20–9.

> Table 20–9: 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: 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, i-sax1dn.p:

> i-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 "i-sax1h-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", "i-sax1-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, i-sax1n.xml:

> i-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, i-sax1hn.p (1 of 2):

> i-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 20–10.

> Table 20–10: 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 © 2005 Progress Software Corporation www.progress.com Voice: (781) 280-4000 Fax: (781) 280-4095