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 .
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.