Computing: DOS, OS/2 & Windows Programming

An introduction to Visual COBOL XML Extensions.

Extensible Markup Language (XML) is the universal format for structured documents and data on the World Wide Web. Adding "structure" to documents facilitates searching, sorting, or any one of a variety of operations that can be performed on an electronic document. XML Extensions is Micro Focus Corporation’s resource that allows Visual COBOL applications to access XML documents.

The major features of Visual COBOL XML Extensions support the ability to import and export XML documents to and from COBOL working storage. Specifically, XML Extensions allows data to be imported from an XML document by converting data elements (as necessary) and storing the results into a matching COBOL data structure. Similarly, data is exported from a COBOL data structure by converting the COBOL data elements (as necessary) and storing the results in an XML document.

This tutorial is a basic introduction to XML Extensions. In fact, it's not more (and not less) than 4 simple programs, that show how to import data from an XML document into a COBOL program, resp. to export data from a COBOL program to XML and HTML. The sample programs have been written and tested on Windows 11 using the 1-year trial version of Visual COBOL for Visual Studio Personal Edition 9.0 (XML Extensions are included with the default installation). The reader is supposed to be familiar with the COBOL programming language, XML, and (for the last example) with XSLT.

Building a COBOL program using the XML Extensions is rather complex, however, this is mostly transparent for the programmer. If you are interested in details, have a look in XML Extensions for Visual COBOL, a booklet that is part of the official product documentation and available as PDF at the Microfocus (resp. OpenText) website.

The XML Extensions are available to the developer for directing the importing and exporting of COBOL data as XML Extensions statements specified in the COBOL program, and implemented in the xmlif COBOL-callable runtime library (on Windows, this library is called cobxmlext.dll).

The interface to the xmlif library is simplified by using some COBOL copy files that perform source text replacement. This means that the developer may use XML Extensions statements, which are much like COBOL statements, rather than writing CALL statements that directly access entry points in the xmlif library. The COBOL copy files also define program variables that are used in conjunction with the XML Extensions statements. To use XML Extensions, the copy file lixmlall.cpy (or at least the copy files referenced by lixmlall.cpy), must be copied to the working-storage section of the program.

A Visual COBOL program using the XML Extensions requires some "standard code", that you'll have to include in all such programs. Here is a skeleton, that you may use as starting point for all of your XML related programs:

       identification division.
       program-id. <Program-name>.
       data division.
       working-storage section.
       --- this program's working-storage data declarations ---
       copy "lixmlall.cpy".
       procedure division.
       A.
          xml initialize.
          if not xml-ok go to Z.
          --- this program's procedure division logic ---
       Z.
          copy "lixmltrm.cpy".
          goback.
          copy "lixmldsp.cpy".
          stop run.
       end program <Program-name>.

The xml initialize statement produces a call to the xmlif library. The statement may be thought of as similar to a COBOL open statement.

Every call to the xmlif library should be followed by a if not xml-ok go to Z statement, where "Z" (or whatever you choose) is the name of the paragraph containing the end-of-program code. This code should be the one shown above, using the two copy files lixmltrm.cpy and lixmldsp.cpy, and being executed for both error and normal termination conditions. The file lixmltrm.cpy is used to generate a correct termination sequence. It includes a call to xml terminate (similar to a COBOL close statement). If there are any errors, the logic in this copy file will perform a procedure defined in the file lixmldsp.cpy, that will display an error message. For details, cf. the XML Extensions documentation.

The following paragraphs contain the code of my 4 XML-related program samples. Click the following link to download the sources (COBOL .cbl files) of the programs. For each of them, you'll have to create a new Visual COBOL project in Visual Studio. Insert (copy/paste) the downloaded code into the .cbl file of the project, and then build the program as described further down in the text.

Program sample 1: Export one file record to XML.

The program sample xml-export.cbl reads a record from an indexed-sequential file and outputs the data read as XML file. The input file used is the amino acids ISAM file aa.dat of my tutorial Handling indexed-sequential files in COBOL (this file is included in the download archive). The output files are XML files with the name of the concerned amino acid.

Here is the code:

       identification division.
       program-id. xml-export.
       author. allu.
       date-written. October 2024.

       environment division.
       input-output section.
       file-control.
           select INFILE assign to "aa.dat"
           organization is indexed
           access is random
           record key is Infile-code1
           alternate key is Infile-code3
           status is Infile-status.

       data division.
       fd INFILE.
       01 Infile-record.
           05 Infile-code1 pic X.
           05 Infile-code3 pic XXX.
           05 Infile-name pic X(13).
           05 Infile-formula1 pic X(10).
           05 Infile-formula2 pic X(32).
       working-storage section.
       77 Program-option pic X.
       77 Infile-status pic XX.
       01 Amino-acid.
           05 Code1 pic X.
           05 Code3 pic XXX.
           05 Name pic X(13).
           05 Formula1 pic X(10).
           05 Formula2 pic X(32).
       copy "lixmlall.cpy".

       procedure division.
       declaratives.
       Infile-errors section.
          use after error procedure
              on INFILE.
       Infile-error.
          display "Error when reading 'aa.dat': " no advancing.
          display Infile-status.
          stop run.
       Infile-error-out.
          exit.
       end declaratives.
       main section.
          xml initialize.
          if not xml-ok
              go to Program-end.
          open input INFILE.
          perform Ask-for-option
              until Program-option = "0".
          close INFILE.
       Program-End.
          copy "lixmltrm.cpy".
          goback.
          copy "lixmldsp.cpy".
          stop run.
       Ask-for-option.
          display "Options: 1=1-letter code, 3=3-letter code ? "
              no advancing.
          accept Program-option.
          if Program-option = "1"
              perform Search-with-1-letter-code
          else
              if Program-option = "3"
                  perform Search-with-3-letter-code
              else
                  xmove "0" to Program-option.
       Search-with-1-letter-code.
          display "1-letter AA code ? " no advancing.
          accept Infile-code1.
          read INFILE
              invalid key
                  perform Key-not-found
              not invalid key
                  perform Export-to-xml.
       Search-with-3-letter-code.
          display "3-letter AA code ? " no advancing.
          accept Infile-code3.
          read INFILE
              key is Infile-code3
              invalid key
                  perform Key-not-found
              not invalid key
                  perform Export-to-xml.
       Export-to-xml.
          move Infile-code1 to Code1.
          move Infile-code3 to Code3.
          move Infile-name to Name.
          move Infile-formula1 to Formula1.
          move Infile-formula2 to Formula2.
          xml export file
              Amino-acid
              Name
              "xml-export#amino-acid".
          if not xml-ok
              move 0 to Program-option.
       Key-not-found.
          display "Invalid or unknown AA code".

Note: When writing the tutorial, I noticed that this code is not as it should be (?). In the case where there is a file error, I terminate the program using a stop run instruction. I suppose that using go to Program-end would be the correct way to do. Unfortunately, I cannot test this, as my Visual COBOL license has meanwhile expired...

The program logic is as follows: The user is asked for an amino-acid code (they may enter the 1-letter or 3-letter code), and the record concerning this amino-acid is read from the ISAM file. After having copied the amino acid data to the working-storage section (not sure if this is mandatory?), the data is exported to an XML file using the instruction xml export file.

This statement has 3 mandatory parameters:

  1. The name of the working-storage section data item, that is to be exported. In our case: "Amino-acid".
  2. The name of the XML output file (the extension .xml is automatically appended to the file name). In our case: the name of the amino acid; "Name" is one of the 05 sub-items of "Amino-acid" and contains the amino acid's name.
  3. The name of the data structure. This name is made of two parts, separated by a "#" symbol. Everything to the right of the "#" is the working-storage data structure name (in our case: "amino-acid"). Everything to the left of the "#" is the name of the XML file generated by the XMLGEN compiler (cf. below). The default filename used by XMLGEN is the name of the COBOL source file (in our case "xml-export"). Thus, in our case, the data structure name to use as 3rd parameter of the xml export file statement is "xml-export#amino-acid".

Building a Visual COBOL program, that uses the XML Extensions, requires 2 steps:

  1. Building the executable. This is done in Visual Studio, just as for any other Visual COBOL program.
  2. Creating the XML model file. This is done in Windows Command Prompt, using the XMLGEN compiler.

The screenshot shows the successful build of the program (the project, in fact) in Visual Studio 2022.

Visual COBOL XML Extensions: Building the project in Visual Studio 2022

This creates the executable xml_export.exe (the hyphen being replaced by an underscore, it seems).

To create the XML model file xml-export.xml, make sure that the "bin" directory of your Visual COBOL installation folder is in the PATH. Open Command Prompt, and cd to the directory that contains the COBOL source file (xml-export.cbl). Then, run the command
    cobol xml-export.cbl xmlgen(ws) noobj;

The screenshot shows the execution of the command, and the resulting XML model file.

Visual COBOL XML Extensions: Creating the XML model file in Windows Command Prompt

For those, who are interested in details, here is the content of xml-export.xml, viewed in Microsoft Edge.

Visual COBOL XML Extensions: Content of a Visual COBOL XML model file (viewed in MS Edge)

To run the program, I put xml_export.exe (I renamed it to xml-export.exe) and xml-export.xml in the folder C:\Data\Programming\Executables, that also contains cblrtsm.dll, and other DLLs needed to run a Visual COBOL program (cf. my tutorial Modern COBOL development using Microfocus Visual COBOL). I also placed there my ISAM data file aa.dat. The screenshot below shows an execution of the program and the XML files created.

Visual COBOL XML Extensions: Running an XML export program [1]

And here, one of these XML files, opened in Microsoft Edge.

Visual COBOL XML Extensions: XML output cretaed by a Visual COBOL program (viewed in MS Edge) [1]

Program sample 2: Export all file records to XML.

The program sample xml-export2.cbl reads all records from the ISAM file aa.dat, and then creates an XML file called aa.xml containing the XML description of my amino-acid data. Here is the code (same remark concerning the stop run statement in case of a file error, as for xml-export.cbl):

       identification division.
       program-id. xml-export2.
       author. allu.
       date-written. October 2024.

       environment division.
       input-output section.
       file-control.
           select INFILE assign to "aa.dat"
           organization is indexed
           access is sequential
           record key is Infile-code1
           alternate key is Infile-code3
           status is Infile-status.

       data division.
       fd INFILE.
       01 Infile-record.
           05 Infile-code1 pic X.
           05 Infile-code3 pic XXX.
           05 Infile-name pic X(13).
           05 Infile-formula1 pic X(10).
           05 Infile-formula2 pic X(32).
       working-storage section.
       77 Infile-eof pic 9.
       77 Infile-status pic XX.
       01 Amino-acids.
           05 Amino-acid occurs 20 indexed by Index-amino-acids.
               10 Code1 pic X.
               10 Code3 pic XXX.
               10 Name pic X(13).
               10 Formula1 pic X(10).
               10 Formula2 pic X(32).
       copy "lixmlall.cpy".

       procedure division.
       declaratives.
       Infile-errors section.
          use after error procedure
              on INFILE.
       Infile-error.
          display "Error when reading 'aa.dat': " no advancing.
          display Infile-status.
          stop run.
       Infile-error-out.
          exit.
       end declaratives.
       main section.
          display "Transforming the ISAM file aa.dat into xml."
          xml initialize.
          if not xml-ok
              go to Program-end.
          open input INFILE.
          move 0 to Infile-eof.
          set Index-amino-acids to 0.
          perform Read-and-copy until Infile-eof = 1.
          close INFILE.
          xml export file
              Amino-acids
              "aa"
              "xml-export2#amino-acids".
       Program-End.
          copy "lixmltrm.cpy".
          goback.
          copy "lixmldsp.cpy".
          stop run.
       Read-and-copy.
          read INFILE
              at end
                  move 1 to Infile-eof
              not at end
                  perform Copy-record.
       Copy-record.
          set Index-amino-acids up by 1.
          move Infile-record to Amino-acid(Index-amino-acids).

The ISAM file is read sequentially and the 20 records read are copied to a 20-elements array. It's the data of this array that is exported to XML, with the creation of a file called "aa" (the extension .xml being automatically added).

Perform the 2-steps build as described further up in the text, and copy the executable together with the model file export-xml2.xml, the required DLL and the ISAM data file, then run the program. The screenshot on the left shows the error message for an execution, where the model file isn't found (because I forgot to copy it). The screenshot on the right shows the successful execution of the program with aa.xml created (aa.txt is the text file that I originally used to create aa.dat).

Visual COBOL XML Extensions: Running an XML export program [2a]
Visual COBOL XML Extensions: Running an XML export program [2b]

And here is the beginning part of the content of aa.xml, opened in Microsoft Edge.

Visual COBOL XML Extensions: XML output cretaed by a Visual COBOL program (viewed in MS Edge) [2]

Program sample 3: Importing data from an XML file.

The program sample xml-import.cbl reads the amino acids XML file, created in the previous example, into a 20-elements array, and then displays the array content (amino acids data) onto the screen.

       identification division.
       program-id. xml-import.
       author. allu.
       date-written. October 2024.

       data division.
       working-storage section.
       01 Amino-acids.
           05 Amino-acid occurs 20 indexed by Index-amino-acids.
               10 Code1 pic X.
               10 Code3 pic XXX.
               10 Name pic X(13).
               10 Formula1 pic X(10).
               10 Formula2 pic X(32).
       01 Display-line.
           05 Display-code1 pic X.
           05 filler pic XX value spaces.
           05 Display-code3 pic XXX.
           05 filler pic XX value spaces.
           05 Display-name pic X(13).
           05 filler pic XX value spaces.
           05 Display-formula1 pic X(10).
           05 filler pic XX value spaces.
           05 Display-formula2 pic X(32).
       copy "lixmlall.cpy".

       procedure division.
       main section.
          xml initialize.
          if not xml-ok
              go to Program-end.
          xml import file
              Amino-acids
              "aa"
              "xml-import#amino-acids".
          if not xml-ok
              go to Program-end.
          display " ".
          perform Display-record
              varying Index-amino-acids from 1 by 1
              until Index-amino-acids > 20.
       Program-End.
          copy "lixmltrm.cpy".
          goback.
          copy "lixmldsp.cpy".
          stop run.
       Display-record.
          move Code1(Index-amino-acids) to Display-code1.
          move Code3(Index-amino-acids) to Display-code3.
          move Name(Index-amino-acids) to Display-name.
          move Formula1(Index-amino-acids) to Display-formula1.
          move Formula2(Index-amino-acids) to Display-formula2.
          display Display-line.

The mandatory parameters of xml import file are similar to those seen before: the working-storage section data item, to where the data is to be imported (in our case: "Amino-acid"), the name of the XML input file (in our case: "aa"; .xml being automatically added to the filename), the name of the data structure (same as before: model file name and the working-storage section data item, separated by a "#" symbol).

Build the executable in Visual Studio, and create the model file xml_import.xml using XMLGEN. Place these files together with the needed DLL and the XML data file. Then, run the program. The screenshot shows the output.

Visual COBOL XML Extensions: Running an XML import program

Program sample 4: Export data file content to HTML.

The program sample xml_export3.cbl reads the content of the ISAM file "aa.dat" into a 20-elements array, then creates a HTML document that lists the 20 amino-acids (as a table).

One of the amazing features of XML is that you can easily transform an XML document to almost any format that you want using XSLT stylesheets. On the Internet, this feature is sometimes used to create a HTML page, displaying data that is stored as XML. That's what we will do here...

We have seen in our 2 first examples that the xml export file statement has 3 mandatory parameters. It has an optional parameter, too: the name of an external XSLT stylesheet, that will be used to transform the generated XML document before it is stored. Creating the HTML list of the amino acids stored in the ISAM file is thus just as simple as creating the XML file. All that we have to do is to write the XSLT stylesheet.

And here it is, my stylesheet file aminoacids.xsl:

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        exclude-result-prefixes="xs"
        version="2.0">
        <xsl:template match="/">
            <html>
                <body>
                    <h2>Amino acids.</h2>
                    <table border="1">
                        <xsl:for-each select="amino-acids/amino-acid">
                            <tr>
                                <td><xsl:value-of select="code1"/></td>
                                <td><xsl:value-of select="code3"/></td>
                                <td><xsl:value-of select="name"/></td>
                                <td><xsl:value-of select="formula1"/></td>
                                <td><xsl:value-of select="formula2"/></td>
                            </tr>
                        </xsl:for-each>
                    </table>
                </body>
            </html>
        </xsl:template>
    </xsl:stylesheet>

Starting from the root of the XML document, the stylesheet creates a <h2> header and a table. In this table, there will be one row for each XML element <amino-acid>, child of <amino-acids>. The different fields in a row will contain the values of the different elements of this amino-acid (<code1>, <code3>, ...).

The code of the program sample "xml_export3.cbl" is identical to the one of "xml_export2.cbl", except for the addition of the XSLT stylesheet name as 4th parameter of the xml export file statement.

       identification division.
       program-id. xml-export3.
       author. allu.
       date-written. October 2024.

       environment division.
       input-output section.
       file-control.
           select INFILE assign to "aa.dat"
           organization is indexed
           access is sequential
           record key is Infile-code1
           alternate key is Infile-code3
           status is Infile-status.

       data division.
       fd INFILE.
       01 Infile-record.
           05 Infile-code1 pic X.
           05 Infile-code3 pic XXX.
           05 Infile-name pic X(13).
           05 Infile-formula1 pic X(10).
           05 Infile-formula2 pic X(32).
       working-storage section.
       77 Infile-eof pic 9.
       77 Infile-status pic XX.
       01 Amino-acids.
           05 Amino-acid occurs 20 indexed by Index-amino-acids.
               10 Code1 pic X.
               10 Code3 pic XXX.
               10 Name pic X(13).
               10 Formula1 pic X(10).
               10 Formula2 pic X(32).
       copy "lixmlall.cpy".

       procedure division.
       declaratives.
       Infile-errors section.
          use after error procedure
              on INFILE.
       Infile-error.
          display "Error when reading 'aa.dat': " no advancing.
          display Infile-status.
          stop run.
       Infile-error-out.
          exit.
       end declaratives.
       main section.
          display "Transforming the ISAM file aa.dat into HTML."
          xml initialize.
          if not xml-ok
              go to Program-end.
          open input INFILE.
          move 0 to Infile-eof.
          set Index-amino-acids to 0.
          perform Read-and-copy until Infile-eof = 1.
          close INFILE.
          xml export file
              Amino-acids
              "aminoacids"
              "xml-export3#amino-acids"
              "aminoacids.xsl".
       Program-End.
          copy "lixmltrm.cpy".
          goback.
          copy "lixmldsp.cpy".
          stop run.
       Read-and-copy.
          read INFILE
              at end
                  move 1 to Infile-eof
              not at end
                  perform Copy-record.
       Copy-record.
          set Index-amino-acids up by 1.
          move Infile-record to Amino-acid(Index-amino-acids).

Note: I named the output file "aminoacids", what results in a file named "aminoacids.xml" (and I manually renamed it to "aminoacids.html"). I suppose that the correct way to do had been to use "aminoacids.html" as filename in the xml export file statement.

Build the executable in Visual Studio, and create the model file xml_export3.xml using XMLGEN. Place these files together with the needed DLL, the stylesheet and the ISAM data file. Then, run the program. The screenshot shows the HTML file generated (aminoacids.html, after I renamed it), opened in MS Edge.

Visual COBOL XML Extensions: HTML output cretaed by a Visual COBOL program + XSLT (viewed in MS Edge)

If you find this text helpful, please, support me and this website by signing my guestbook.