Creating an RXS Template

To generate XML, RPG-XML Suite relies on what is referred to as the “template engine” or “composition engine”. This engine uses a specially marked up psuedo-XML file divided into sections and with variable fields embedded in it as well as a set of RPG-XML Suite subprocedures. The combination of the template file and the subprocedures allow you to build XML of any complexity or depth needed to meet your business requirements.

To start with creating a template file, we need to create a file in the IFS. This command uses QSHELL to create an IFS stream file using CCSID 819. Note that this command is case-sensitive.

QSH CMD('touch -C 819 /www/myrxs/templates/bldtpl001.xml')

Next, we need to populate this file with a sample of the XML that we’re going to use as the basis of the template. Note that to make your life easier, you want this to be as complete an example as possible. If your XML has many optional fields, try to include them all in this sample file, even if it some of them would not logically appear with others in normal usage. To get the XML into our IFS stream file, we’ll use the EDTF command.

EDTF '/www/myrxs/templates/bldtpl001.xml'

Now, paste your sample XML into the resulting editor window. For the sake of this example, we’ll use the following XML:

<PostAdr residential="true">
  <name title="Mr.">
    <first>Sample</first>
    <last>Resident</last>    
  </name>
  <street>999 Totally A Real Street</street>
  <city>Cityville</city>
  <state>OH</state>
  <zip>12345</zip>
  <phone>123-456-7890</phone>
  <phone>987-654-3210</phone>
</PostAdr>

While in the Edit File editor select F2 to save the document changes. Now it is time to invoke the BLDTPL command to generate a *.tpl file that will facilitate composing the PostAdr XML document.

BLDTPL IFSXMLLOC('/www/myrxs/templates/bldtpl001.xml') IFSTPLLOC('/www/myrxs/templates/bldtpl001.tpl') INDENT(2)

The IFSXMLLOC parameter is the location of the XML file that was created in the above steps. The IFSTPLLOC is where we want the new bldtpl001.tpl file to be located – place it in the location of all other RPG-XML Suite templates. The last parameter, INDENT, allows you to specify how the template’s XML should be indented. Note that indenting is purely for ease of editing.

See below for the resulting template file located at /www/myrxs/templates/bldtpl001.tpl - use EDTF to open it. Note that all elements and attributes have had their values replaced with variable place holders (i.e. .:variable:.) respective to the element or attribute’s name.

<PostAdr residential=".:residential:.">
  <name title=".:title:.">
    <first>.:first:.</first>
    <last>.:last:.</last>
  </name>
  <street>.:street:.</street>
  <cty>.:cty:.</cty>
  <state>.:state:.</state>
  <zip>.:zip:.</zip>
  <phone>.:phone:.</phone>
</PostAdr>

An important thing to note is that in the generated template file, there is only one <phone> element, where before there were two. This is because of the other main concept used in RXS template files - sections. A section is a portion of a template file that is referred to by name. Sections can be written out multiple times in an XML document. Sections are also not created by BLDTPL - you will need to go in and add them yourself based on your knowledge and understanding of how the document is meant to be composed and would best be divided. Below is an example of how to split up our example XML into sections:

::PostAdr_beg
<PostAdr residential=".:residential:.">
  <name title=".:title:.">
    <first>.:first:.</first>
    <last>.:last:.</last>
  </name>
  <street>.:street:.</street>
  <cty>.:cty:.</cty>
  <state>.:state:.</state>
  <zip>.:zip:.</zip>
::phone
  <phone>.:phone:.</phone>
::PostAdr_end
</PostAdr>

By giving the <phone> element a separate section, it gives us the flexibility to write as many of those elements as we need - our document could have one phone number, or a thousand phone numbers - or even none! Not all sections in an XML document need to be used during XML composition. Sections of your XML may be optional - unused depending upon your business logic or a specific request’s needs.

Next, we’re going to use the CRTRPGTPL command introduced in RXS 3.0x to convert our template file from an IFS stream file into an RPG source member that will be compiled directly into your RPG-XML Suite-enabled RPG program. We’re going to use the command to create the new RXS template in our RXS library in the QRPGLETPL source physical file.

Note: the command will try to supply a name of *FIRST for the member. You need to change this to a name that makes sense. We generally recommend to use the same name as the program you’re going to use this template with.

CRTRPGTPL STMF('/www/myrxs/templates/bldtpl001.tpl') FILE(RXS/QRPGLETPL) MBR(POSTADR) SECTIONSDS(S) VARSDS(V)

It’s also important to note that we’re specifying values for SECTIONSDS and VARSDS. This will group all of the various fields and sections into qualified data structures and help keep the template engine fields separate from your normal RPG code. This is not required, but strongly recommended.

Once this command has completed, you can open up the source physical file RXS/QRPGLETPL and look at member POSTADR using PDM or RDi/RDp/WDSC. It should look like this:



/IF NOT DEFINED(POSTADR)
 /DEFINE POSTADR
D S...
D                 DS                  Qualified
D  PostAdr_beg...
D                               50A   Varying
D  phone...
D                               50A   Varying
D  PostAdr_end...
D                               50A   Varying
D V...
D                 DS                  Qualified
D  residential...
D                               30A   Varying
D  title...
D                               30A   Varying
D  first...
D                               30A   Varying
D  last...
D                               30A   Varying
D  street...
D                               30A   Varying
D  city...
D                               30A   Varying
D  state...
D                               30A   Varying
D  zip...
D                               30A   Varying
D  phone...
D                               30A   Varying
 /EOF
 /ENDIF
 /free

    S.PostAdr_beg =
      'PostAdr_beg';
    S.phone =
      'phone';
    S.PostAdr_end =
      'PostAdr_end';
    V.residential =
      'residential';
    V.title =
      'title';
    V.first =
      'first';
    V.last =
      'last';
    V.street =
      'street';
    V.city =
      'city';
    V.state =
      'state';
    V.zip =
      'zip';
    V.phone =
      'phone';

   p  = '';
   p += '::PostAdr_beg' + x'15';
   p += '<PostAdr residential="' + '.:residential:.' + '">' + x'15';
   p += '  <name title="' + '.:title:.' + '">' + x'15';
   p += '    <first>' + '.:first:.' + '</first>' + x'15';
   p += '    <last>' + '.:last:.' + '</last>' + x'15';
   p += '  </name>' + x'15';
   p += '  <street>' + '.:street:.' + '</street>' + x'15';
   p += '  <city>' + '.:city:.' + '</city>' + x'15';
   p += '  <state>' + '.:state:.' + '</state>' + x'15';
   p += '  <zip>' + '.:zip:.' + '</zip>' + x'15';
   p += '::phone' + x'15';
   p += '  <phone>' + '.:phone:.' + '</phone>' + x'15';
   p += '::PostAdr_end' + x'15';
   p += '</PostAdr>' + x'15';

 /end-free

As you can see, the resulting template file is quite literally RPG code. It specifies fields and the original IFS template in a particular way to allow the composition engine to function. One of the benefits of this approach is that because template sections and template variables are RPG fields, it’s very easy to catch typos in your program because the program simply won’t compile.

Note: you should not manually modify template files created with CRTRPGTPL - it is very easy to make a mistake. Instead, modifications should be made to the .tpl file in the IFS, and then CRTRPGTPL should be run again.

Next, you’ll need to bring this template into an RPG program, and use the composition engine subprocedures to build your resulting XML. An example of this is below:

Example Code

H DEBUG(*YES) DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

 /copy QRPGLECPY,RXSCB
 /copy QRPGLETPL,POSTADR

D Template        PR
D  p                                  Like(RXS_TEMPLATE_PARM)

D XML             S                   Like(RXS_Var64Kv_t)
D i               S              3I 0

D ComposeDS       DS                  LikeDS(RXS_ComposeDS_t)
D                                     Inz(*LikeDS)

 /free
   monitor;

     RXS_ResetDS( ComposeDS : RXS_DS_TYPE_COMPOSE );
     ComposeDS.TemplateProcedure = %Paddr(Template);
     RXS_StartComposeEngine( ComposeDS );

     RXS_ComposeVariable( V.residential : 'true');
     RXS_ComposeVariable( V.title : 'Mr.' );
     RXS_ComposeVariable( V.first : 'Sample' );
     RXS_ComposeVariable( V.last : 'Resident' );
     RXS_ComposeVariable( V.street : '999 Totally A Real Street' );
     RXS_ComposeVariable( V.city : 'Cityville' );
     RXS_ComposeVariable( V.state : 'OH' );
     RXS_ComposeVariable( V.zip : '12345' );
     RXS_ComposeSection( S.PostAdr_beg );

     for i = 1 to 3 by 1;
       RXS_ComposeVariable( V.phone : '987-654-321' + %Char(i) );
       RXS_ComposeSection( S.phone );
     endfor;

     RXS_ComposeSection( S.PostAdr_end );

     XML = RXS_GetComposeBuffer();

     RXS_JobLog( XML );
   on-error;

   endmon;
   *Inlr = *On;

 /end-free


P Template        B
D                 PI
D  p                                  Like(RXS_TEMPLATE_PARM)
 /copy QRPGLETPL,POSTADR
P                 E
H DEBUG(*YES) DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

 /define RXSV6R1
 /copy QRPGLECPY,RXSCB
 /copy QRPGLETPL,POSTADR

D Template        PR
D  p                                  Like(RXS_TEMPLATE_PARM)

D XML             S                   Like(RXS_Var64Kv_t)
D i               S              3I 0

D ComposeDS       DS                  LikeDS(RXS_ComposeDS_t)
D                                     Inz(*LikeDS)

 /free
   monitor;

     RXS_ResetDS( ComposeDS : RXS_DS_TYPE_COMPOSE );
     ComposeDS.TemplateProcedure = %Paddr(Template);
     RXS_StartComposeEngine( ComposeDS );

     RXS_ComposeVariable( V.residential : 'true');
     RXS_ComposeVariable( V.title : 'Mr.' );
     RXS_ComposeVariable( V.first : 'Sample' );
     RXS_ComposeVariable( V.last : 'Resident' );
     RXS_ComposeVariable( V.street : '999 Totally A Real Street' );
     RXS_ComposeVariable( V.city : 'Cityville' );
     RXS_ComposeVariable( V.state : 'OH' );
     RXS_ComposeVariable( V.zip : '12345' );
     RXS_ComposeSection( S.PostAdr_beg );

     for i = 1 to 3 by 1;
       RXS_ComposeVariable( V.phone : '987-654-321' + %Char(i) );
       RXS_ComposeSection( S.phone );
     endfor;

     RXS_ComposeSection( S.PostAdr_end );

     RXS_GetComposeBuffer( XML );

     RXS_JobLog( XML );
   on-error;

   endmon;
   *Inlr = *On;

 /end-free


P Template        B
D                 PI
D  p                                  Like(RXS_TEMPLATE_PARM)
 /copy QRPGLETPL,POSTADR
P                 E

The end result of this program will be XML written out to the job log that looks like the following:

<PostAdr residential="true">
  <name title="Mr.">
    <first>Sample</first>
    <last>Resident</last>    
  </name>
  <street>999 Totally A Real Street</street>
  <city>Cityville</city>
  <state>OH</state>
  <zip>12345</zip>
  <phone>987-654-3211</phone>
  <phone>987-654-3212</phone>
  <phone>987-654-3213</phone>
</PostAdr>