Try OpenEdge Now
skip to main content
Working with XML
Reading XML Documents with the Simple API for XML (SAX) : Developing ABL SAX applications : Example code: retrieving names and phone numbers : Without namespace processing
 
Without namespace processing
In this example of a SAX driver procedure, i-sax1d.p, the driver's logic closely parallels the tasks in Basic tasks of OpenEdge SAX application.

i-sax1d.p

DEFINE VARIABLE hHandler AS HANDLE NO-UNDO.
DEFINE VARIABLE hParser  AS HANDLE NO-UNDO.

/* 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.
This is the associated sample XML file, i-sax1.xm.. 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>
i-sax1h.p is the sample handler procedure with SAX callbacks, i-sax1h.p.

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 */
DEFINE VARIABLE currentPerson AS CHARACTER NO-UNDO.

/* Phone number from the current entry. App gets it during the Characters
   callback because it is the character data for the element. */
DEFINE VARIABLE currentNum    AS CHARACTER NO-UNDO.

/* 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 NO-UNDO.
DEFINE INPUT PARAMETER localName    AS CHARACTER NO-UNDO.
DEFINE INPUT PARAMETER qname        AS CHARACTER NO-UNDO.
DEFINE INPUT PARAMETER hAttributes  AS HANDLE    NO-UNDO.

IF qName = "Entry" THEN
currentPerson = hAttributes:GET-VALUE-BY-QNAME("ContactName").
END PROCEDURE.

/* 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 ABL CHARACTER variable limit) */
PROCEDURE Characters:
DEFINE INPUT PARAMETER charData AS MEMPTR  NO-UNDO.
DEFINE INPUT PARAMETER numChars AS INTEGER NO-UNDO.

/* 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 PROCEDURE.

/* 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 NO-UNDO.
DEFINE INPUT PARAMETER localName    AS CHARACTER NO-UNDO.
DEFINE INPUT PARAMETER qName        AS CHARACTER NO-UNDO.

IF qName = "Entry" THEN DO:
MESSAGE "Name: " currentPerson SKIP
"Phone Number: " currentNum VIEW-AS ALERT-BOX.
    ASSIGN
      currentNum    = ""
      currentPerson = "".
END.
END PROCEDURE.

/* 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 PROCEDURE.
When the sample is run, it produces the following trace:

Trace of SAX driver without namespace processing

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