With Extensible Markup Language (XML) in all the industry rags and most examples being written in Java or C++, RPG programmers may feel a bit left out. After all, how can you determine when and how to use XML if you cant play with it?
An Open Invitation to the Sandbox
Not to be remiss in its duty and allegiance to the developer community that made the AS/400, IBM, using C as the ever-dependable diplomat, has opened the door to those RPG IV developers who have not yet found an overriding reason to migrate their skills wholly to
Java and want to play with XML. IBM alphaWorks (www.alphaworks.ibm.com) provides a set of C-wrappers for C++ class and object methods that implement the Document Object Model (DOM) and Simple API for XML (SAX) 1.0 interfaces defined by the World Wide Web Consortium (W3C). This gives RPG developers the ability to build documents for a variety of applications with a requirement to communicate with applications written in different languages, on disparate sets of computer platforms, without requiring middleware. For instance, XML documents can be transformed into browser-based (HTML) or PDF reports, exchanged with other participants in an Internet electronic data interchange (EDI) network, or used to synchronize the security operating environment of AS/400s in a network of AS/400s. The list of XML-based applications is endless and grows daily. The benefit of adopting XML comes with the promise of eliminating middleware (when acting purely as a data broker) and its associated costs (both in financial and performance terms). In this article, I will show you how to develop RPG IV programs that use the DOM API 1.0, and give you valuable information about SAX (in the Web sidebar RPG IV and SAX: Let Me Know When We Get There! at www. midrangecomputing.com/mc) and about how such services may satisfy current requirements.
Why the Neat Features Support RPG IV and Not RPG/400
Before I begin my example, I want to answer two common questions: Why do I have to use RPG IV? and Why cant I just use my old trustworthy RPG/400? A brief explanation of the DOM and SAX APIs will help answer those questions. Using the DOM API, a
representation of the document is created in memory as a tree with a trunk (root node), branches (sometimes referred to as a parent, or document, node but also as a child of the root node), and leaves (text nodes). Everything in DOM and SAX is referred to as a node. Ponder that for a moment while looking out the window of your office or home and ask yourself, How would I write a program that could give me access to the characteristics (size, shape, color) of a particular leaf (in a group of leaves) on a particular branch offshoot (in a group of branches) from a tree trunk? You quickly begin to see the distinctly recursive nature of such a process. Now ask yourself, Does RPG/400 allow recursively called subroutines? By contrast, RPG IV suprocedures can be called recursively. If you just dont like RPG IV, you can also use C or COBOL to employ the same DOM APIs that I demonstrate using RPG IV.
With such a document tree in memory, you can perform actions on any node in the document in any order. You can move forward and backward in the tree and randomly insert, remove, or modify nodes. The SAX API, on the other hand, does not build a document tree in memory but instead lets you set callbacks for certain document events. Actions are performed by callback procedures (you have written) as you encounter nodes that interest you (for which you set procedure callbacks); but, once the node is passed, you cannot traverse backward, because the document is not in memory. So why use SAX over DOM if DOM is so much more flexible? Because a 40 Mb document (as might be the case with a large report) can create expensive overhead and make for quite a CPU-intensive applicationcommonly referred to as a CPU hog. Plus, you cannot process anything until the entire document is loaded and built into memory.
A DDS by Any Other Name or DTD
As you can see, XML has an unlimited number of potential applications. However, it is best suited as a platform-independent, industry-standard method or protocol for data exchange. A key criterion for ensuring good value in an XML data exchange is the presence of a standard set of semantics. In XML, that means defining a set of industry- standard document type definitions (DTDs). DTDs are used by a validating parser to ensure that, when a document is received, it has the expected format defined by that DTD. For example, suppose you need a security report that rates the risk of security-related system values against a corporate security policy defined in accordance with an internal audit policy. The XML document to represent the policy might look like the one shown in Figure
1.
However, to ensure that the XML policy document defined in Figure 1 uses a format that can be freely exchanged with other interested parties, common semantics must be adopted and understood. The DTD in Figure 2 can be passed to a validating parser along with the XML document to ensure a proper and fruitful exchange. Examining the DTD in Figure 2, you see that the entire document is identified as SecurityReport. Next, a row is defined as a composition of SystemValue, Value, and Points (Note that #PCDATA simply means parsed char-acter data).
Yet, if you compare the DTD shown in Figure 2 with the XML document in Figure 1, you see the last system value recommendation (QPWDLMTREP) would be rejected as an invalid element (FromValue and ToValue are not defined in the DTD). However, if you make a slight change to the DTD like the one shown in Figure 3, the document passes validation.
Specifying the or (|) symbol allows either Value or both FromValue and ToValue to be used in the same document for any given row. This allows you to use either a single value or a range of values for testing compliance to the policy. You can view the XML document as a data set, with rows being records, and the DTD as an enlightened DDS.
DOM in 21 Minutes and 10 Easy Steps
Now that the preliminaries are out of the way, I will show you more specific applications of the DOM APIs, using the XML document and the DTD presented thus far. I have adapted some code examples from the alphaWorks Web site for my security policy reporting applications that you get with the required download.
Roughly 237 functions are contained in the XML4PR310 service program and deal with DOM or SAX in some manner. My examples deal only with those typically used in the normal course of applications development using the DOM API and are presented in Figure 4 (page 50). None of the examples are presented in their entirety. However, the complete source can be downloaded from the MC Web site (www.midrangecomputing. com/mc). One program, SEC0001R (partially shown in Figure 5 (page 52), demonstrates how to create an XML document using DOM APIs by reading system value recommended settings from a policy file (SVSVF), creating an XML document tree with those values, and reading the DOM tree to write the document to a stream file for communicating the policy to other AS/400s for scoring compliance. When running this program, the one and only argument is the fully qualified file name on the AS/400 Integrated File System (AS/400 IFS) where the policy XML document is written. Here is an example:
Call SEC0001R parm(/home/jdb/mypolicy.xml)
Another program, SEC0002R, reads the stream file created by SEC0001R, creates an XML DOM document tree in memory (by a process known as parsing), reads the DOM tree document elements (walks the tree), retrieves the related system values on the current system, scores those values against the values in the XML document, inserts the score of the current system (based on possible points earned) into the XML document, and writes out the new XML document to a stream file. When running this program, the first argument can be -v to call the validating parser. The second argument can be the fully qualified file name on the IFS where the input policy file created from SEC0001R resides, and the third argument can be the fully qualified file name (on the AS/400 IFS) where the scored compliance XML document will be written. This example is for calling the nonvalidating parser:
Call SEC0002R parm(/home/jdb/myscore.xml +
/home/jdb/myscore2.xml)
This example is for calling the validating parser:
Call SEC0002R parm(-v /home/jdb/myscore.xml +
/home/jdb/myscore2.xml)
Other article materials are the SQL necessary to create the security policy table and insert the related records, sample XML document output from both programs (one nonvalidating, one validating), and the DTDs for validation tests (an invalid DTD for the policy XML, a valid DTD for the policy XML, and a valid DTD for the compliance-scoring XML). Ensure that these materials are in the same directory before running the examples. (DTDs should be in the same directory as related XML documents.)
Building a DOM Tree from Scratch
To create the XML document to be written to a stream file, read the values presented in Figure 1 from the system values policy file, SVSVF. This file has four fields: SystemValue name, FromValue (which doubles as Value in a single value rating), ToValue, and Points
(i.e., the points given when the rated AS/400 values pass the test). This file, along with the rest of the code for this article, can be downloaded from the MC Web site (www. midrangecomputing.com/mmu).
Step 1: Set a Pointer to the DOM Exceptions Feedback Area
The first step in creating an XML document is to get the pointer (Figure 5, Section A) to the Qxml_DOMExcData data structure (defined in QXML4PR310 headers, provided in the alphaWorks download), which contains feedback information about the success or failure of DOM API calls, and pass it to the QxmlInit function (as shown in Figure 5, Section B) before any DOM functions are called.
Proper error-handling can be ensured by checking the return code (Qxml_DOMRtnCod) after execution of a DOM API method and taking the appropriate action. A list of what each return code means is contained in the QxmlPR4310 copy member, defined as integer constants.
Step 2: Create a Document Implementation
Next, create a document implementation with a call to QxmlDOM_DOMImplementation_ new (Figure 5, Section C). The document implementation is an object representing the current DOM implementation (that is, the fulfillment of abstract interfaces defined by the W3C), which is currently locked into the XML4C (XML for C++) DOM API 1.0 developed by alphaWorks. XML4C is available on the AS/400 as C language APIs in the XML4PR310 service program. The only method allowed on this object is the hasFeature(feature:version) method, used to determine if a feature being used in a bound application can be supported by the current DOM implementation. For programs written in Java, this method provides a valuable service with implementations of DOM API beyond
1.0. However, considering that procedural applications are tightly bound to a version of XML4PR310 that is tightly bound to a version of XML4C, this service is of little use to procedural applications. Regardless, the pointer returned from the call to this method is passed to the Qxml_DOMImplementation_create-Document method (Figure 5, Section D).
Step 3: Create a Document
The next step creates an instance of a document type by calling the QxmlDOM_DocumentType_new method (Figure 5, Section C). At current, this method simply allocates space for the document type node in the document hierarchy, but no methods exist to specify the DTD on the creation of the document. The Qxml_DOMImplementation_createDocument method is as follows:
Eval DomDoc@ =
Qxml_DOMImplementation_createDocument
(DomImpl@:
DOMString1@:
Qxml_InDOMStr:
0:
strgbuf@ :
Qxml_CharStr:
0:
DomDoct@)
This method returns a pointer to a document node (DomDoc@), used later in the program, upon taking the following as arguments:
a pointer (DomImpl@) to a DOM implementation
a pointer (DOMString1@) to an empty null-terminated string representing the document namespace(a unique identifier to avoid DTD naming conflicts)
an integer constant (Qxml_InDOMStr) representing the data type of the string (DOMString1@) defined for the second argument
when 0, the namespace length is a null-terminated character string
a pointer (strgbuf@) to a string buffer representing the top-level document name (in my example XML, that is SecurityReport)
integer constant (Qxml_CharStr) representing the data type of the top-level document name string buffer name
when 0, the top-level document name length is a null-terminated character string
a pointer (DomDoct@) to a document type instance created with QxmlDOM_DocumentType_new
Because they are not needed after the document is created, the implementation, document type, and DOMString objects are deleted (Section E of Figure 5). This deletion step is a recurring requirement with DOM objects. Once they become part of the DOM structure, the objects are discarded with a call to Qxml
Step 4: Get a Pointer to the Document Root
The call to QxmlDOM_Document_get-DocumentElement(DomDoc@), passing a pointer to the document node (Section F), returns a pointer to the root node. (You have to start building from the bottom up.) With the root element in hand, SEC0001R is ready to iteratively read records from the policy file (SVSVF).
Step 5: Create an Element Node for row
The read loop begins by creating (Figure 5, Section G) another DOMString object with the row tag text, using the DOMString_new method:
Eval DomString1@ = QxmlDOMString_new
(strgbuf@:
Qxml_CharStr:
0)
The first argument contains a pointer to a null-terminated string (strgbuf@) with the row text. Argument 2 contains an integer constant defined in the QxmlPR310 /COPY member (Qxml_CharStr) that defines the codepage encoding scheme as English EBCDIC
(37). Finally the last argument, which is 0, identifies the string in the first argument as a null-terminated string. This is a good time to draw attention to an extension (not defined in DOM) made by the AS/400 alphaWorks folks that enables better internationalization support in a multilingual applications environment by use of the QxmlTranscode method for transcoding from one codepage to another. This differs from the QxmlDOMString_transcode method that only allows transcoding to the default CCSID of the job from a DOM string.
A pointer to the DOMString object created in Section G is then passed to the create element method in Section H:
Eval row@ =
QxmlDOM_Document_createElement
(DomDoc@:
DOMString1@:
Qxml_INDOMSTR:
0)
This action creates the row tag element node. It returns a pointer (row@) to the row document element node.
Step 6: Append the Element Node (row) to the Document
However, the element node is not yet a part of the document. To make it a part of the document, call the append child method (Figure 5, Section I):
Eval child@ = QxmlDOM_Node_appendChild
(RootElem@:
row@)
This method specifies the pointer to the root node (RootElem@) as the first argument and the pointer to the newly created element node for row (row@) as the second argument to attach the row node to the tree. SEC0001R then does some more cleanup work in Figure 5, Section J, with calls to Qxml
Step 7: Create an Element Node for SystemValue
In Figure 5, Section K, another DOMString is created with the text SystemValue, which becomes the first child node of the row node by creating an element with a call to the QxmlDOM_Document_createElement() method in Section L and passing it the SystemValues DOMString object just created.
Step 8: Append the Element Node (SystemValue) to the Document
Lastly, in Section M, the append child method is called:
Eval child@ = QxmlDOM_Node_appendChild
(row@:
SysValElem@)
This action passes the System Values tag node, created in Section L, as the second argument (SysValElem@), to be appended to the row node as the first argument (row@), created in Section H.
Step 9: Create a Text Node for QSECURITY and Append It to the SystemValue Element Node
Now, if you think about the XML document in Figure 1 and mentally traverse the branches, you see the next step in the process. You need to create the text portion of the system value element you just added. In the case of the first row, that is the QSECURITY text value. You do this by creating a text node with a call to the create text node method in Section N:
Eval SysValName@ = QxmlDOM_Document_createTextNode
(DomDoc@: strgbuf@:
qxml_CHARSTR:
%size(SvSvVN))
This passes a pointer to the XML document (DOMDoc@) node as the first argument, a pointer to the string buffer containing the QSECURITY value as the second argument, a constant (Qxml_CharStr) representing the encoding scheme (37) as the third argument, and an integer representing the size of the text string as the fourth and final
argument. This method returns a pointer to the text node created for QSECURITY (SysValName@) that is passed to the QxmlDOM_Node_appendChild() method in Section O, along with the System Value element node (SysValElem@) created in Section L, to append the text node to the element node. Since the System Value element node has already been appended to the row node in Section M, you again need to clean up objects, in Section P, that are no longer required after the nodes have been appended.
Step 10: Go Back to Step One for Each Document Element
Now step back for a moment, and you can see the newly constructed branch with one leaf node. Repeating this sequence of steps for Value (or FromValue and ToValue, if a range test is desired), then ending with the Points element, you can construct the entire row (or branch cluster) for a System
Value policy recommendation such as QSECURITY. Iterate again and again until all policy value recommendations have a unique document row node (or branch cluster) constructed and appended to the tree. Thats all there is to it!
Writing the Document to a File
Once the DOM tree has been built for the XML document in Figure 1, the SEC0001R program recursively iterates through the document and writes the resulting document to a stream file. A necessary first step in accomplishing that task is the creation of the DOM tree node list by a call to getElementsByTagName.
Eval DomNodeList@ =
QxmlDOM_Document_getElementsByTagName
(DomDoc@:
strgbuf@:
Qxml_CHARSTR:
0)
This method is shown in Section Q. The string buffer passed as the second argument contains an asterisk (*) that specifies retrieval of all elements from the document node (the second argument) indicated. With a pointer to the NodeList returned in Section Q, you can get the number of elements in the document by calling the getLength method shown in Section R.
Eval elemCount =
QxmlDOM_NodeList_getLength
(DomNodeList@)
0)
This assures you that a valid document does indeed exist in memory. Next, as you proceed through the tree, three methods can be called to gather information for decision- making or conditional treatment. The getNodeName method returns the name of the node. For example, for an element node such as SysValueElem@, it returns System-Value; for row@, it returns row; for text nodes such as SysValName@, it returns #text; and for a comment, it returns #comment.
Eval nodeName@ =
QxmlDOM_Node_getNodeName(
DOMNode@)
The getNodeValue method returns the value of a node. For example, for a text node such as SysValName@, it returns SystemValue; for an element node such as SysValElem@, it returns null.
Eval nodEval@ =
QxmlDOM_Node_getNodeValue(
DOMNode@ )
Finally, getNodeType method returns an integer value representing the type of node associated with a given node pointer. For example, text nodes have an integer value of 3. The QxmlPr310 /COPY member has a defined set of constants that can be used in your application. For text nodes, the constant is Qxml_Text_Nod. This is extremely useful and required when determining subsequent action to be taken in your application.
Eval nodeType = QxmlDOM_Node_getNodeType
( DOMNode@ )
To proceed through the tree in a recursive manner, interrogating nodes at each level, you can use a couple of stepwise methods. The first method (getFirstChild) will get the child for a given node specified by the one and only argument. The getFirstChild method in Figure 5, Section S, is followed by the getNextSibling method.
Eval DOMChild@ =
QxmlDOM_Node_getFirstChild( DOMNode@)
Eval DOMChild@ =
QxmlDOM_Node_getNextSibling(DOMChild@)
These methods provide the drilldown capability required to properly manipulate the tree. These two methods are contained in a procedure that is called recursively to discover all elements and text nodes in each branch cluster, beginning with the first node representing the document node (DomDoc@).
Finally, Figure 5, Section T, shows the basic methods used to write the resulting XML document to a stream file, using the QxmlOpenNewOutPutStream method to open a stream file, QxmlWriteOutPutStream() to write records to the file, and QxmlCloseOutPutSteam() to close the file. These methods are not integral to an understanding of the DOM APIs, so I simply acknowledge their existence in this article. However, note that these and other extension methods, contained in the XML4PR310 API, have a different naming convention from the DOM API methods in that they do not have any underscore separators in their name.
What Was Your Grade?
Now that you have read the security policy information from the file (SVSVF) and created an XML document, the next step is to take the policy document created from SEC0001R and score an existing AS/400s current settings. To make this exercise easy to work through and to focus strictly on the DOM API, SEC0002R contains compile-time alternating arrays in the program source, rather than creating an additional requirement to extract system value information you may not have access to. These arrays contain the system value name and the presumed earned points for that value (no tests are made).
SEC0002R demonstrates five additional methods disclosed in Figure 4. The first is instrumental in creating a DOM tree from an existing XML document. The QxmlDOMParser_new creates a pointer to a specific implementation of a parser. In this case, it is the XML4C parser:
Eval
DomParse@=QxmlDOMParser_new(PrsData@)
Before using the parser object to parse a document, you can specify that you want to use the validating parser by calling the setDoValidation method, as shown here:
CallP QxmlDOMParser_setDoValidation(
Domparse@:
valmode)
The validation mode can be set to 0 or 1 (thats right, off or on). The must have already been embedded in the XML document being parsed.
Next, to actually parse the document and build the DOM tree, you call the QxmlDOMParser_parse_SystemId method, which takes the following arguments:
a pointer to a parser object
a pointer to a null-terminated string representing the qualified stream file name
a constant integer representing the CCSID of the string defined for the second argument
when 0, specifies that the string defined in the second argument is null-terminated. This is required if a CCSID is specified for the third argument.
CallP QxmlDOMParser_parse_SystemId(
DomParse@:
XmlFile@:
Qxml_CCSID37:
0)
Once the XML document has been parsed and before it can be manipulated, the getDocument() method must be called to get a pointer to the document node:
Eval DomDoc@ =
QxmlDOMParser_getDocument( DomParse@)
With a pointer to the document node in hand, manipulation can begin with a call to the getElementsByTagName method previously described.
The final methods to be explored are the getParentNode and insertBefore methods. These two methods are used in conjunction in SEC0002R to get the parent of the last element node (points) in a row and to insert the Earned Points element and text nodes immediately before that last node, where SVNode@(index) is an array that contains pointers to each of the points nodes in the XML document.
Eval parentNode@ =
QxmlDOM_Node_getParentNode(SVNode@(index))
Eval child@ = QxmlDOM_Node_insertBefore(
parentNode@:
epointsElem@:
SVNode@(index))
Getting It Right!
XML for RPG had two DOM API implementationsone embodied in a service program (XMLRPG), dubbed XML for RPG, and this latest version (XML4PR310), called XML for Procedural Interface languages. The former, which is no longer available, was designed to be semantically distinctive for an RPG flavor (shortened, all uppercase function names)
but was not intuitive for those who had used the Java APIs or bound to C functions with RPG IV. The latter implementation is a more respectful depiction of what you would need to do if you were calling similar functions in C++ or Java (i.e., better mapping). Since I believe that most RPG IV developers will be doing a mix of programming language development, this eases the thought transition. In fact, this article and this API will interest precisely this type of programmer. These APIs are quite involved and can require tedious programming, so they are not meant for the casual programmer.
I applaud IBM alphaWorks for implementing an accurate and well-performing representation of the interfaces defined by the W3C for the DOM and SAX 1.0 APIs. I further bestow accolades upon it for bringing the XML technology to AS/400 procedural developers. After all, who is more familiar with the data makeup of a legacy system than a legacy developer? (I use that adjective respectfully.) These developers need the capability of creating and sending XML documents to client and other distributed host applications. While the XML is an evolving technology, with new standards submissions each day, my opinion is that the XML for Procedural Interface service program (XMLPR310) is ready for prime time. The underlying XML4C implementation is currently shipped as a part of the AS/400 WebSphere Commerce product. Be adventurous, fearless, and have fun. Thats my motto! The XML for Procedural Interface will give you a glimpse into what lurks beyond the darkness and will accentuate your other development senses. Can you XML-it yet?
REFERENCES AND RELATED MATERIALS
Applied XML: A Toolkit for Programmers. Alex Ceponkus and Faraz Hoodbhoy. New York, New York: John Wiley & Sons, Inc., 1999
Professional Java XML Programming with Servlets and JSP. Alexander Nakhimovsky and Tom Myers. Chicago, Illinois: Wrox Press Inc., 1999
XML and Java: Developing Web Applications. Hiroshi Maruyama, Kent Tamura, and Naohiko Uramoto. Boston, Massachusettes: Addison-Wesley, 1999
Figure 1: An XML document can contain structured information about anything, such as AS/400 system values.
Figure 2: DTDs are used to validate the correctness of an XML file.
Figure 3: Think of a DTD as a schema for a specific XML database.
DOM API Description
QxmlInit Initializes DOM and SAX exception data structures and provides pointer for feedback in the current thread
QxmlDOM_DOMImplementation_new Creates an instance representing the current implementation of the DOM API in service QxmlDOM_DocumentType_new Creates an instance of DTD (null when nonvalidating). Allocates header space in resultant
XML document QxmlDOMString_null Creates a DOMString object with no (null) content QxmlDOM_DOMImplementation_createDocument Creates an instance of a DOM document based on the current DOM implementation and document type
QxmlDOM_DOMImplementation_delete Deletes the implementation object when no longer required QxmlDOM_DocumentType_delete Deletes the document type object when no longer required QxmlDOM_Document_getDocumentElement Returns the root element of a document
QxmlDOMString_new Creates a 16-bit string (Unicode)
QxmlDOM_Document_createElement Creates an element child node QxmlDOM_Node_appendChild Appends a child node to the document QxmlDOM_Node_delete Deletes a node object QxmlDOMString_delete Deletes a DOMString object QxmlDOM_Document_createTextNode Creates a text child node QxmlDOM_Text_delete Deletes a text object QxmlDOM_Element_delete Deletes an element object QxmlDOM_Document_getElementsByTagName Returns a pointer to a NodeList of the current document elements QxmlDOM_NodeList_delete Deletes the node list object from memory QxmlDOM_NodeList_getLength Returns the number of elements in the node list created by the getElementsByTagName method QxmlDOM_Node_getNodeName Returns a pointer to a DOM string containing the name of the element QxmlDOM_Node_getNodeValue Returns a pointer to a DOM string containing the value of the element (text node value) QxmlDOM_Node_getNodeType Returns an integer representing the type of document node being interrogated. These types can be,
Element, text nodes, attributes, comments, processing instructions, etc. The list is shown in the /COPY member XML4PR310
QxmlDOM_Node_getFirstChild Returns the first child for a node (like setll and reade)
QxmlDOM_Node_getNextSibling Returns the next sibling (like the subsequent reade after entering a loop) QxmlDOM_Node_isNull Tests the value returned from the getNextSibling method for terminating the loop QxmlDOM_Node_getAttributes Returns an attributes list for an element QxmlDOM_NamedNodeMap_getLength Returns the number of nodes in a map
QxmlDOM_NamedNodeMap_item Retrieves the name of a specified node
QxmlOpenNewOutPutStream Opens a stream file for writing an XML document
QxmlWriteOutPutStream Writes XML data to a stream file
QxmlCloseOutPutStream Closes an XML stream file
QxmlGenPrint Outputs text for display to stdout
QxmlTransCode Encodes data with the specified CCSID. Primarily for mixed national languages support QxmlDOMString_TransCode Encodes data with the implementation CCSID only
QxmlDelete_AllocStr Deletes a transcoded DOM string
QxmlDOM_Node_getParentNode Returns the Parent node for a specified sibling
QxmlDOM_Node_insertBefore Inserts a node before the referenced node
QxmlDOMParser_new Creates a parser object to parse an existing XML document QxmlDOMParser_setDoValidation Sets the validation mode of a parser object
QxmlDOMParser_parse_SystemId Parses an XML document; creates a DOM tree for the document
QxmlTerm Removes your thread from the managed return code areas
Figure 4: The DOM APIs are available to RPG programmers from the XML4PR310 service program.
LATEST COMMENTS
MC Press Online