Consuming an XML service with COBOL

By | February 24, 2014

Now that we can make HTTP web requests directly from our COBOL program and do this with SSL Security, we should be able to use these skills access a service that returns non-SOAP XML.  JSON is preferable these days, however we can leverage the native COBOL parsing support for XML and, as far as I know, there is no native JSON parser as yet.

I’m starting with the COBOL SSL program I posted earlier.  Much of the program is the same.  I am going to access one of the Google APIs, the Geocoding API, which takes an HTTP query string request and returns an XML document.  (Note that if you use this API in a business application, you should become a Maps for Business customer.)  It would not be too difficult to generate an XML request for some service using the COBOL XML Generate support.

Some issues I encountered in this project:

  • The Google API returns a chunked response, so multiple gsk_secure_socket_read calls are required (which I put in a perform loop).
  • Through trial and error, I decided to determine the end of the response by looking for a contiguous pair of carriage-return linefeed combinations.
           perform with test after
                   until  ws-reply (ws-reply-tail-offset : 4)
                          = ws-double-cr-lf-ascii

      *      gsk_status (*gsk_secure_socket_read) (
      *        gsk_handle              soc_handle,
      *        char *                  buffer,
      *        int                     size,
      *        int *                   length);

             compute ws-reply-length = length of ws-reply - ws-offset

             call 'gsk_secure_socket_read'
               using by value          ws-gsk-soc-handle
                     by value          address of ws-reply (ws-offset :)
                     by value          ws-reply-length
                     by value          address of ws-reply-length
               returning               ws-rc

             if ws-rc not = zero
             then
               move z'gsk_secure_socket_read'
                                     to ws-gsk-function
               go to gsk-error
             end-if

             compute ws-reply-tail-offset =
               ws-offset + ws-reply-length - 4

             add ws-reply-length to ws-offset

           end-perform
  • Using the XMLPARSE(XMLSS) compiler option, the XML parser expects the document to be in the host CCSID and it ignores the encoding attribute in the XML header.
  • I had to locate the start of the XML document inside of the HTTP response otherwise the XML PARSE verb generated a strange exception.
  • I identified the end of the XML document as being when the highest level group element closed as indicated by the generated indentation retuning to the base level.  Setting XML-CODE to -1 ends the parse operation.
  • The COBOL XML PARSE verb seems to treat some white-space data (blanks and newline characters) as data, so, for example,
    <abc>
      <def>foo</def>
    </abc>

    seems to return a value of x’154040′ for abc and “foo” for def.

      * Find beginning of document

           perform
             varying ws-offset from 1 by 1
             until   ws-reply (ws-offset : 6) = '<?xml '
           end-perform

      * apparently x'15' is treated as data, not white-space

           inspect ws-reply (ws-offset :)
             replacing all x'15' by ' '

           move +1 to ws-indent

           xml parse ws-reply (ws-offset :)
             processing procedure goecoderesponse-handler
             on exception
               if xml-code not = -1
                 display 'XML document error ' xml-code
               else
                 move zero to return-code
               end-if
           end-xml
           .

       goecoderesponse-handler.

           evaluate xml-event
             when 'START-OF-ELEMENT'
               display ws-blank (1 : ws-indent)  xml-text ':'
               compute ws-indent = ws-indent + 2
             when 'CONTENT-CHARACTERS'
               if xml-text not = spaces
               then
                 display ws-blank (1 : ws-indent)  xml-text
               end-if
             when 'END-OF-ELEMENT'
               compute ws-indent = ws-indent - 2
               if ws-indent = 1
                 move    -1  to  XML-CODE
               end-if
             when 'EXCEPTION'
               display 'Exception '  xml-code
                       ' at offset ' length of xml-text '.'
           end-evaluate
           .

Here is the complete source: cobxml.cbl. Compile as in the earlier COBSSL program.

This version does not handle chunked data very well as it assumes the returned XML will be all in one chunk, which is not always the case.

Leave a Reply

Your email address will not be published. Required fields are marked *