XML-INTO And Optional Elements
February 23, 2016 Hey, Jon
Note: The code accompanying this article is available for download here.
I am working on an RPG program that does some very basic pre-processing of an XML document before passing it along to another program for final processing. Even though my program works, it fills up the job log with error messages. Can you help me get rid of them?
The XML is structured like this.
<Message> <Payload> <Event>. . . </Event> </Payload> </Message>
The <Payload> element can contain a child element of <Event>, <Report>, or <Parameter>. Only one of these elements is ever contained in the message at a time. If it is an <Event>, I call PGMA. If it is a <Report>, I call PGMB. If the element is a <Parameter>, I call PGMC.
I am using the following XML-INTO to check for the existence of the element.
XML-INTO(e) Event %XML(msgText:'allowmissing=yes path=Message/Payload/Event'); if not %error; call PGMA (msgText); else; XML-INTO(e) Report %XML(msgText:'allowmissing=yes path=Message/Payload/Report'); if not %error; call PGMB (msgText); else; XML-INTO(e) Parameter %XML(msgText:'allowmissing=yes path=Message/Payload/Parameter'); if not %error; call PGMC (msgText); endif; endif; endif;
This works fine. When no errors are found the <Event> element exists and PGMA is called. If an error is returned I check for the <Report> element and call PGMB, and if it’s not found I check for <Parameter> and call PGMC.
The downside to this approach is that lots of error messages are generated in the job log each time the XML-INTO fails. The messages read: “The XML document does not match the RPG variable; reason code 1.” (RNX0353). I hoped the “allowingmissing=yes” option would suppress these errors but it doesn’t.
Can you suggest how I can determine whether an element exists while avoiding these errors in the job log? I’d like to avoid using XML-SAX since that seems more complex and I’m only interested in knowing if a particular element exists. I don’t care about the element values in this program, but only whether a particular child element exists.
First of all let me say that your original solution of using the error condition was novel. I confess that I don’t think it would have occurred to me to do it that way. I’ll talk a little more about the problems (other than error messages in the logs) of that approach after I’ve given you my suggested solutions.
The basic recipe for the “secret sauce” to making this work was described in Part 2 and Part 3 of my XML-INTO series in Four Hundred Guru. I confess, though, that I did not emphasize its use in this kind of situation.
The option you need to use is countprefix. While this was primarily designed to determine how many of a specific repeating element were in the document, it can also be used to determine if any optional element is present or not.
Here is a modified version of your data structure to show how the definitions of the counts would look.
Dcl-ds Payload; Event char(nn); count_Event int(5); Report char(nn); count_Report int(5); Parameter char(nn); count_Parameter int(5); End-ds;
And the XML-INTO that uses this would change to:
XML-INTO Payload %XML(msgText:'countprefix=count_ path=Message/Payload');
After the XML-INTO has completed, you simply test the “count_” variables to determine if an element was present or not. Like so:
If count_Event <> 0; // call event routine ... ElseIf count_Report <> 0; // call report routine ElseIf count_Parameter <> 0; // call parameter routine Else; /// Error condition EndIf;
The reason this works without the “allowmissing=yes” option is that RPG only considers elements to be “missing” if it has no way to tell you otherwise. By supplying the counts you give it a way to inform you as to whether the element is present or not, and so it will not generate an error.
Let me add a couple of additional notes.
You said in your email that you didn’t care about the element values, but I would suggest that it might be a good idea to have the XML-INTO completely process the document and then pass the resulting data structure component(s) to the programs you are calling. If you don’t want to do this, you will have to add “allowextra=yes” because without it, the compiler will treat the child elements of the <Event>, <Report>, and <Parameter> elements as an error condition.
“Allowextra” is safe enough to use in most cases such as this, but never use “allowmissing” unless you have absolutely no other choice. It is a very dangerous option. The reason is, with that option specified, the entire document could be empty and no error would be signaled! Back in V5R4 we had no choice, but now we have better options.
I have a sample RPG program, in both fixed and free formats, that demonstrates the basics of the technique as outlined. If you would like to have them, you can find the code here.
One final comment on your original error triggering approach. You should be aware that it only works in a simple case such as this one. For example, had Message or Payload been repeating elements (or nested within some other repeating element) it could not have been used. The solution I have presented here will work under all conditions.
In many ways, particularly if you really don’t want to parse the child elements of <Event>, <Report>, and <Parameter>, XML-SAX would probably have been the best approach. Watch for a future tip on the topic and you’ll see what I mean. However, it occurs to me that the simplest of all approaches to this particular problem might simply be to use %Scan to see if the required element is present. For example:
If %Scan( '<Report>': Payload ) <> 0; // process report ElseIf %Scan( '<Event>': Payload ) <> 0; // process event etc. etc.
I hope this helps.
Jon Paris is one of the world’s most knowledgeable experts on programming on the System i platform. Paris cut his teeth on the System/38 way back when, and in 1987 he joined IBM’s Toronto software lab to work on the COBOL compilers for the System/38 and System/36. He also worked on the creation of the COBOL/400 compilers for the original AS/400s back in 1988, and was one of the key developers behind RPG IV and the CODE/400 development tool. In 1998, he left IBM to start his own education and training firm, a job he does to this day with his wife, Susan Gantner–also an expert in System i programming. Paris and Gantner, along with Paul Tuohy and Skip Marchesani, are co-founders of System i Developer, which hosts the new RPG & DB2 Summit conference. Send your questions or comments for Jon to Ted Holt via the IT Jungle Contact page.