Ever just have one of those days. You come into work, ready to get started on the day’s development. You’ve got a nice cup of coffee, maybe even a donut (or whatever your regional breakfast drink and food of choice is). The next thing you know your phone is ringing. It’s your Basis group on the line letting you know that one of YOUR programs is creating short dumps in THEIR system. OK, its probably user error. You log in and have a look at ST22. 300 dumps, 50 different users – probably not user error.
Who made the change?
So you have a look at the BSP application that is having the problem. According to version management it hasn’t changed in over 4 months. The problem has to do with an invalid parameter on a call to an RFC. But wait, the RFC hasn’t been changed in over 4 months either. It turns out that someone added a field to the end of a structure that was being used in a table type in one of the exporting parameters of the RFC. Everything tested fine with the programs and RFCs in R/3 because all the objects shared the same table type definition. Unfortunetly no one told the stand alone WebAS about this change. This turned out to be an easy fix – just adjust the definition of the parameter that had originally been generated by the BAPI Browser (See Last Weblog if you are confused by the term BAPI Browser). However I wanted to find some way to avoid this problem in the future. Next time it happens it might not be the morning, but rather the middle of the night when the problem occurs.
XML to the Rescue
The idea we came up with was to use XML. Instead of predefined interface parameters, we would just return an XML stream on the RFC. The calling program could then deserialze the XML and pick out the data it wanted. More than one program could share the same XML interface. They could add addtional fields without fear of breaking any other calling programs. The following is what the XML call itself looks like:
data: i_xml type xstring. call function 'Z_E_RFC_GET_PERSON_TRAIN' destination rfcdest exporting uname = wa_itab-uname importing xml = i_xml exceptions others = 1.
Let’s look at this example in more detail. You can probably get an idea of what we are doing by looking at this function call alone. We want to take a user id and look up all the HR Personnel and Training information for this user and display it to them on a BSP page. This was actually the RFC call that caused the original problem. Someone had added a new field from HR that wasn’t used in this BSP page but was needed elsewhere.
Creating the XML
First let’s examine the ABAP code in R/3 that we will use to create the XML stream. First we looked at what SAP had to offer for working with XML. What you are going to want to look at is everything in the SIXML development class. (I should note that this code is all based upon a 46C system. I’m not sure that the iXML class libraries that contain the basic serialization functionality are available before 46C.) In this development class you will find all the interfaces, classes, and example programs you will need to get started.
We start the processing by creating a pointer to the XML library and creating an empty XML document:
* create document ixml = cl_ixml=>create( ). document = ixml->create_document( ).
Next we are ready to create a new element in our XML document:
data: jobs_element type ref to if_ixml_element. call method document->create_element exporting name = 'JOBS' receiving rval = jobs_element. call method document->append_child exporting new_child = jobs_element.
Now that we have an element you can create some attributes of this element:
clear value. move jobs_line-objid to value. call method jobs_element->set_attribute exporting name = 'OBJID' value = value.
Next we can serialize the data. Because the serialization is performed by the Kernel and not in ABAP you have the option of choosing UTF-8 or Unicode as your output format even on a non-Unicode system. The data will be passed back to ABAP as a byte stream so the Unicode format will be maintained. This is what serialization logic looks like:
* write data into string data: s_encoding_type type string. streamfactory = ixml->create_stream_factory( ). s_encoding_type = encoding_type. encoding = ixml->create_encoding( character_set = s_encoding_type byte_order = 0 ).
Now we will give SAP a pointer to the output area for the serialized data. In this case it is a variable called b_xml. This is a variable defined as a XString (Byte String):
ostream = streamfactory->create_ostream_xstring( b_xml ).
Finally we tell SAP that we are ready to render the XML into the output area:
call method ostream->set_encoding exporting encoding = encoding. call method document->render exporting ostream = ostream recursive = 'X'. ressize = ostream->get_num_written_raw( ).
Consuming the XML
Well we have our XML data as a byte string and we have passed that byte string back to our BSP application. Now why don’t we have a look at how this XML string can be turned back into something a little more usable in ABAP.
First we have a few useful data declarations:
data: i_xml type xstring. data: ixml type ref to if_ixml. data: document type ref to if_ixml_document. data: streamfactory type ref to if_ixml_stream_factory. data: istream type ref to if_ixml_istream. data: parser type ref to if_ixml_parser. data: element type ref to if_ixml_element.
Just like in the outbound example we will start by creating a instance of the XML factory class and a XML Document:
*-- create the main factory ixml = cl_ixml=>create( ). *-- create the initial document document = ixml->create_document( ).
Next we will take that byte stream that we received and feed it into the XML parser:
streamfactory = ixml->create_stream_factory( ). istream = streamfactory->create_istream_xstring( i_xml ).
Now we can tell SAP to parse the XML:
parser = ixml->create_parser( stream_factory = streamfactory istream = istream document = document ). parser->parse( ).
With the parsing done, we can go back through the document and pull out the pieces of data that we are interested in.
element = document->get_root_element( ). fname = element->get_attribute( name = 'VORNA' ).
Are we there yet?
Now you may be saying to yourself that this was a lot of work. The code samples can be a little overwhelming at first, I admit. Believe me that boiling them down for presentation in the weblog format was quite a challenge. I hope that these sample explanations have been helpful. I can tell you that the best way to learn it is to do it. After a little programming time with the XML interface it really becomes quite simple. Not only is this tool a solution to the problem put forth in the weblog, but is useful to know in other instances. With this you can parse XML from many different external sources. You will also see later when we discuss the IGS Charting, that XML processing in ABAP is quite important. The other thing that I should mention is that XML is perfect for passing hierarchical data(like HR data). This way you avoid multiple tables that need to be linked up or complex table types. As you process through your deserialized data you have the perfect opportunity to build a tree control from the data using the XML structure itself to control parent/child relationships.
For those of you who may feel that this weblog was a little light on actual BSP, just wait. The next edition of this weblog will dive right into the deep end as we take apart a productive BSP application, looking especially at custom BSP Extensions. It should be lots of fun!