XMLParserCallbacks Class
The XMLParserCallbacks class enables the parsing of very large XML documents. It does this by processing individual elements during the parsing phase instead of waiting until parsing is complete.
To achieve this element-level processing, OpenROAD calls a user-defined 4GL handler procedure at the start or end of each XML element found during document parsing. This approach allows processing of large XML documents without producing out-of-memory situations. This is because the user-defined 4GL procedure processes each parsed element so that the method doing the parsing can safely remove the XMLElement from its parent (ParentElement), allowing for efficient memory use while processing the XML document.
Use the XMLDocument.ParserCallbacks attribute (see
ParserCallbacks Attribute) to reference the XMLParserCallbacks object that contains a reference to user-defined 4GL procedures, which OpenROAD calls at specific points during parsing. You specify these points by setting any of the following handler attributes:
• EndDocumentHandler
• EndElementHandler
• StartDocumentHandler
• StartElementHandler
These attributes have a data type of ProcHandle, which can be set to a user-defined 4GL procedure. This procedure definition must have a named parameter called ParserCallbacks that has a data type of XMLParserCallbacks. OpenROAD passes the correct value to this procedure while parsing the XML document. This allows the user-defined 4GL procedures to access attributes that contain the current XML document (CurDocument) and XML element (CurElement) during parsing.
When the ParserCallbacks attribute of an XMLDocument is set to an XMLParserCallbacks object and that XMLParserCallbacks object contains references to one or more ProcHandles representing user-defined 4GL procedures, then while executing the XMLDocument methods ParseURL or ParseString, OpenROAD calls these handlers at appropriate points during parsing of the XML document with a statement like this:
ProcHandle.Call(ParserCallbacks = XMLParserCallbacks);
Note: The RootElement of the XMLDocument is set only after parsing completes successfully. Therefore, it is NULL while the handler procedures run.
Because OpenROAD calls each handler procedure as an exclusive call, restrictions apply. These restrictions are identical to those for ProcExec.DataEntryErrorHandler() (see
DataEntryErrorHandler Attribute):
• Each procedure must not call, go to, or open any frames.
• Each procedure must not invoke any method that waits for any window system event, user event, or database event.
These restrictions also apply to any procedures or methods that the procedure calls.
This user-defined 4GL procedure returns an INTEGER NOT NULL to provide a status result:
• ER_OK indicates that parsing will continue.
• ER_FAIL indicates that parsing aborts.
The invoking ParseURL or ParseString method returns a value specified by the XMLParserCallbacks' ParseReturnValue attribute:
• ER_OK indicates success.
• ER_FAIL indicates an error condition about which additional information might be available in XMLDocument.Errortext if the user-defined 4GL handler set the XMLParserCallbacks.Errortext attribute.
Note: Within the user-defined 4GL handler procedure, XMLParserCallback.ParseReturnValue and XMLParserCallback.Errortext can be set.
Inherits From
Inherited By
None
Attributes
CurDocument
CurElement
EndDocumentHandler
EndElementHandler
Errortext
ParseReturnValue
StartDocumentHandler
StartElementHandler
Methods
None
Example 1: XMLParserCallbacks
While parsing any XML document, it is possible that OpenROAD will call back into your application to allow user-defined procedures to run at well-defined points in the processing:
• After parsing the start of the XML document (after the <?xml …?> XML declaration, if any), OpenROAD checks to see if a ProcHandle exists in the StartDocumentHandler attribute. If it finds one, it calls back into the application to run this 4GL procedure one time. If this attribute is not set, there is no user-defined 4GL procedure to call, so processing continues.
• After parsing the starting tag of any XML element, OpenROAD checks to see if a ProcHandle exists in the StartElementHandler attribute. If it finds one, it calls back into the application to run this 4GL procedure. This procedure will run multiple times if more than one StartElement element exists in the XML document. If this attribute is not set, there is no user-defined 4GL procedure to call, so processing continues.
• After parsing the ending tag of any XML element, OpenROAD checks to see if a ProcHandle exists in the EndElementHandler attribute. If it finds one, it calls back into the application to run this 4GL procedure. This procedure will run multiple times if more than one EndElement element exists in the XML document. If this attribute is not set, there is no user-defined 4GL procedure to call, so processing continues.
• When it reaches the end of the XML document, OpenROAD checks to see if a ProcHandle exists in the EndDocumentHandler attribute. If it finds one, it calls back into the application to run this 4GL procedure. This procedure will run only once, as there is only one EndDocument element in an XML document. If this attribute is not set, there is no user-defined 4GL procedure to call, so processing continues.
Because it is possible to nest XML elements, the calls of the StartElementHandler and EndElementHandler procedures could occur in different sequences. For example, while parsing an XML file with the following content:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<OPENROAD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<COMPONENT name="perf" xsi:type="proc4glsource">
<script>
<![CDATA[procedure perf() =
declare
d = date not null;
enddeclare
{
select date('now') as :d;
return d;
}
]]>
</script>
<datatype>date</datatype>
</COMPONENT>
</OPENROAD>
if the XMLParserCallbacks object contains the following ProcHandles:
DECLARE
xd = XmlDocument;
xpc = XmlParserCallbacks;
url = varchar(1000);
ENDDECLARE
{
.
.
.
xpc.StartDocumentHandler = CurSession.Scope.GetProcHandle(name = 'procStartDocument');
xpc.EndDocumentHandler = CurSession.Scope.GetProcHandle(name = 'procEndDocument');
xpc.StartElementHandler = CurSession.Scope.GetProcHandle(name = 'procStartElement');
xpc.EndElementHandler = CurSession.Scope.GetProcHandle(name = 'procEndElement');
xd.ParserCallbacks = xpc;
IF xd.ParseUrl(urllocation = url) = ER_OK THEN
.
.
.
}
Then, while the ParseURL method parses the above-mentioned XML file, OpenROAD calls the user-defined 4GL procedures in this sequence:
If any of XMLParserCallback's attributes are not set, OpenROAD does not call back into the application at those points and continues to parse the document. In this way you can decide what is important to handle while parsing the document.
Using the return code of the 4GL procedure, you can abort processing after finding pertinent information. For instance, if you are interested only in the name of the first COMPONENT within the file, you would use a StartElementHandler procedure to check if ParserCallbacks.CurElement.Name = 'COMPONENT'. If this condition is ever TRUE, you then use GetAttributeValue on the element to save the value. You can write the found name to some global variable (or to ClientData of the CurDocument), so it becomes available after parsing, and set the ParseReturnValue to ER_OK (to indicate no error), but return ER_FAIL to immediately abort the parsing.
In this case, the 4GL procedure referenced by the StartElementHandler attribute is executed twice—once for:
<OPENROAD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
and once for:
<COMPONENT name="perf" xsi:type="proc4glsource">
Example 2: XMLParserCallbacks
This example demonstrates the processing of a large XML document. The rows, represented by XML elements with the name "row" (and their columns represented by XML attributes), are written into a database table "mytab" with colums "id" and "value" during parsing. To achieve this, take the following steps:
1. Create a global 4GL procedure "handleRows" with the following script:
procedure handleRows(parsercallbacks = XmlParserCallbacks) =
declare
el = XMLElement DEFAULT NULL;
v_id = VARCHAR(2000) NOT NULL;
v_value = VARCHAR(2000) NOT NULL;
enddeclare
{
el = parsercallbacks.CurElement;
IF el.Name = 'row' THEN
el.ParentElement = NULL;
el.GetAttributeValue(name = 'id', value=BYREF(v_id));
el.GetAttributeValue(name = 'value', value=BYREF(v_value));
INSERT INTO mytab(id, value) VALUES(int4(:v_id), :v_value);
IF iierrornumber <> 0 THEN
parsercallbacks.Errortext = 'DB Error: ' + varchar(iierrornumber);
parsercallbacks.ParseReturnValue = ER_FAIL;
RETURN ER_FAIL;
ENDIF;
ENDIF;
RETURN ER_OK;
}
2. Parse the file using an XMLParserCallbacks object that uses the "handleRows" procedure as EndElementHandler:
DECLARE
xd = XMLDocument;
xpc = XMLParserCallbacks;
ENDDECLARE
{
xpc.EndElementHandler = CurSession.Scope.GetProcHandle(name = 'handleRows');
xd.ParserCallbacks = xpc;
IF xd.ParseUrl(urllocation='mydata.xml') = ER_OK THEN
COMMIT; // Commit the inserts done during parsing
ELSE
MESSAGE xd.Errortext;
ENDIF;
}