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 : With namespace processing
 
With namespace processing
This section shows another version of the driver example where the XML document uses namespaces. Consequently, the StartElement and EndElement callbacks in the handler procedure use the namespaceURI and localName parameters rather than the qName parameter.
Note: The original example could have used localName by itself, but did not.
i-sax1dn.p is the SAX driver procedure with namespace processing.

i-sax1dn.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-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.
i-sax1n.xml is the associated XML file with namespaces.

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>
i-sax1hn.p is the handler procedure with namespace processing.

i-sax1hn.p

/* Name attribute for the current entry. App gets it during the StartElement
   callback */
DEFINE VARIABLE currentPerson AS CHARACTER NO-UNO.

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

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

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

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

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

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.
      ASSIGN
        currentNum    = ""
        currentPerson = "".
    END.
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 this driver with namespace processing is run, it produces the following trace:

Trace of SAX driver 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