VMS Tech Demo 12 - XML-RPC direct XML

Content:

  1. Introduction
  2. Wire XML
  3. Java, Python, PHP etc.
  4. C
  5. Pascal
  6. Fortran
  7. Basic

Introduction:

The previous article VMS Tech Demo 11 - XML-RPC used XML-RPC libraries. This article will format and parse XML directly.

Wire XML:

We will use the same examples as in VMS Tech Demo 11 - XML-RPC.

XML is formatted to improve readability.

method request response flavor A response flavor B
getInt
<methodCall>
    <methodName>Test.getInt</methodName>
    <params></params>
</methodCall>
<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
    <params>
        <param>
            <value>
                <i4>123</i4>
            </value>
        </param>
    </params>
</methodResponse>
<?xml version='1.0'?>
<methodResponse>
    <params>
        <param>
            <value>
                <int>123</int>
            </value>
        </param>
    </params>
</methodResponse>
getString
<methodCall>
    <methodName>Test.getString</methodName>
    <params></params>
</methodCall>
<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
    <params>
        <param>
            <value>ABC</value>
        </param>
    </params>
</methodResponse>
<?xml version='1.0'?>
<methodResponse>
    <params>
        <param>
            <value>
                <string>ABC</string>
            </value>
        </param>
    </params>
</methodResponse>
getData
<methodCall>
    <methodName>Test.getData</methodName>
    <params></params>
</methodCall>
<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
    <params>
        <param>
            <value>
                <struct>
                    <member>
                        <name>sval</name>
                        <value>ABC</value>
                    </member>
                    <member>
                        <name>ival</name>
                        <value>
                            <i4>123</i4>
                        </value>
                    </member>
                </struct>
            </value>
        </param>
    </params>
</methodResponse>
<?xml version='1.0'?>
<methodResponse>
    <params>
        <param>
            <value>
                <struct>
                    <member>
                        <name>ival</name>
                        <value>
                            <int>123</int>
                        </value>
                    </member>
                    <member>
                        <name>sval</name>
                        <value>
                            <string>ABC</string>
                        </value>
                    </member>
                </struct>
            </value>
        </param>
    </params>
</methodResponse>
getListOfInts
<methodCall>
    <methodName>Test.getListOfInts</methodName>
    <params></params>
</methodCall>
<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
    <params>
        <param>
            <value>
                <array>
                    <data>
                        <value>
                            <i4>123</i4>
                        </value>
                        <value>
                            <i4>456</i4>
                        </value>
                    </data>
                </array>
            </value>
        </param>
    </params>
</methodResponse>
<?xml version='1.0'?>
<methodResponse>
    <params>
        <param>
            <value>
                <array>
                    <data>
                        <value>
                            <int>123</int>
                        </value>
                        <value>
                            <int>456</int>
                        </value>
                    </data>
                </array>
            </value>
        </param>
    </params>
</methodResponse>
getListOfStrings
<methodCall>
    <methodName>Test.getListOfStrings</methodName>
    <params></params>
</methodCall>
<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
    <params>
        <param>
            <value>
                <array>
                    <data>
                        <value>ABC</value>
                        <value>DEF</value>
                    </data>
                </array>
            </value>
        </param>
    </params>
</methodResponse>
<?xml version='1.0'?>
<methodResponse>
    <params>
        <param>
            <value>
                <array>
                    <data>
                        <value>
                            <string>ABC</string>
                        </value>
                        <value>
                            <string>DEF</string>
                        </value>
                    </data>
                </array>
            </value>
        </param>
    </params>
</methodResponse>
getListOfData
<methodCall>
    <methodName>Test.getListOfData</methodName>
    <params></params>
</methodCall>
<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
    <params>
        <param>
            <value>
                <array>
                    <data>
                        <value>
                            <struct>
                                <member>
                                    <name>sval</name>
                                    <value>ABC</value>
                                </member>
                                <member>
                                    <name>ival</name>
                                    <value>
                                        <i4>123</i4>
                                    </value>
                                </member>
                            </struct>
                        </value>
                        <value>
                            <struct>
                                <member>
                                    <name>sval</name>
                                    <value>DEF</value>
                                </member>
                                <member>
                                    <name>ival</name>
                                    <value>
                                        <i4>456</i4>
                                    </value>
                                </member>
                            </struct>
                        </value>
                    </data>
                </array>
            </value>
        </param>
    </params>
</methodResponse>
<?xml version='1.0'?>
<methodResponse>
    <params>
        <param>
            <value>
                <array>
                    <data>
                        <value>
                            <struct>
                                <member>
                                    <name>ival</name>
                                    <value>
                                        <int>123</int>
                                    </value>
                                </member>
                                <member>
                                    <name>sval</name>
                                    <value>
                                        <string>ABC</string>
                                    </value>
                                </member>
                            </struct>
                        </value>
                        <value>
                            <struct>
                                <member>
                                    <name>ival</name>
                                    <value>
                                        <int>456</int>
                                    </value>
                                </member>
                                <member>
                                    <name>sval</name>
                                    <value>
                                        <string>DEF</string>
                                    </value>
                                </member>
                            </struct>
                        </value>
                    </data>
                </array>
            </value>
        </param>
    </params>
</methodResponse>
add
<methodCall>
    <methodName>Test.add</methodName>
    <params>
        <param>
            <value>
                <i4>123</i4>
            </value>
        </param>
        <param>
            <value>
                <i4>456</i4>
            </value>
        </param>
    </params>
</methodCall>
<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
    <params>
        <param>
            <value>
                <i4>579</i4>
            </value>
        </param>
    </params>
</methodResponse>
<?xml version='1.0'?>
<methodResponse>
    <params>
        <param>
            <value>
                <int>579</int>
            </value>
        </param>
    </params>
</methodResponse>
concat
<methodCall>
    <methodName>Test.concat</methodName>
    <params>
        <param>
            <value>
                <string>ABC</string>
            </value>
        </param>
        <param>
            <value>
                <string>DEF</string>
            </value>
        </param>
    </params>
</methodCall>
<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
    <params>
        <param>
            <value>ABCDEF</value>
        </param>
    </params>
</methodResponse>
<?xml version='1.0'?>
<methodResponse>
    <params>
        <param>
            <value>
                <string>ABCDEF</string>
            </value>
        </param>
    </params>
</methodResponse>
modify
<methodCall>
    <methodName>Test.modify</methodName>
    <params>
        <param>
            <value>
                <struct>
                    <member>
                        <name>ival</name>
                        <value>
                            <i4>123</i4>
                        </value>
                    </member>
                    <member>
                        <name>sval</name>
                        <value>
                            <string>ABC</string>
                        </value>
                    </member>
                </struct>
            </value>
        </param>
    </params>
</methodCall>
<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
    <params>
        <param>
            <value>
                <struct>
                    <member>
                        <name>sval</name>
                        <value>ABCX</value>
                    </member>
                    <member>
                        <name>ival</name>
                        <value>
                            <i4>124</i4>
                        </value>
                    </member>
                </struct>
            </value>
        </param>
    </params>
</methodResponse>
<?xml version='1.0'?>
<methodResponse>
    <params>
        <param>
            <value>
                <struct>
                    <member>
                        <name>ival</name>
                        <value>
                            <int>124</int>
                        </value>
                    </member>
                    <member>
                        <name>sval</name>
                        <value>
                            <string>ABCX</string>
                        </value>
                    </member>
                </struct>
            </value>
        </param>
    </params>
</methodResponse>

We see that:

We see that:

To keep things simple we will only do the two first groups and not the last group. It is possible to use structs using direct XML - the code will just become very messy.

We will use a simple HTTP library. Any HTTP library capable of sending POST requests should be fine. I use the same HTTP library as in previous article.

XML formatting is done by just inserting values in hardcoded XML.

XML "parsing" is done by a grab function that simply grabs everything between a start tag and an end tag.

The code should be practically identical across all languages shown.

Note that the grab function is written to be simple and easily implementable in any language. It can be optimized in most languages.

The servers are the same Java and Python servers as used in VMS Tech Demo 11 - XML-RPC.

Java, Python, PHP etc.:

The direct XML approach is certainly possible in these languages, but given the existence of excellent libraries also on VMS, then there is no point.

C:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "vms_http.h"

const int NONGREEDY = 0;
const int GREEDY = 1;

static int grab(const char *xmlstr, int *start, int *end, const char *tag, int greedy)
{
    char starttag[32], endtag[32];
    snprintf(starttag, sizeof(starttag), "<%s>", tag);
    snprintf(endtag, sizeof(endtag), "</%s>", tag);
    int ixstart, ixend;
    ixstart = *start;
    while((ixstart < (strlen(xmlstr) - strlen(starttag))) && (memcmp(xmlstr + ixstart, starttag, strlen(starttag)) != 0)) ixstart++;
    ixstart += strlen(starttag);
    if(greedy)
    {
        ixend = *end - strlen(endtag);
        while((ixend > 0) && (memcmp(xmlstr + ixend, endtag, strlen(endtag)) != 0)) ixend--;
    }
    else
    {
        ixend = ixstart;
        while((ixend < (strlen(xmlstr) - strlen(starttag))) && (memcmp(xmlstr + ixend, endtag, strlen(endtag)) != 0)) ixend++;
    }
    if((ixstart < (strlen(xmlstr) - strlen(starttag))) && (ixend >= 0))
    {
        *start = ixstart;
        *end = ixend;
        return 1;
    }
    else
    {
        return 0;
    }
}

void test(const char *host, int port)
{
    printf("http://%s:%d/ (C)\n", host, port);
    {
        char req[1000];
        snprintf(req, sizeof(req), "<methodCall><methodName>%s</methodName><params></params></methodCall>", "Test.getInt");
        struct vms_http *ctx = http_post(host, port, "/", "text/xml", "text/xml", req);
        char resp[10000];
        short resplen;
        http_recv_all(ctx, resp, sizeof(resp), &resplen);
        resp[resplen] = 0;
        http_close(ctx);
        int ixstart = 0;
        int ixend = strlen(resp) - 1;
        if(grab(resp, &ixstart, &ixend, "value", GREEDY))
        {
            if(grab(resp, &ixstart, &ixend, "i4", GREEDY))
            {
                int v = atoi(resp + ixstart);
                printf("%d\n", v); 
            }
            else if(grab(resp, &ixstart, &ixend, "int", GREEDY))
            {
                int v = atoi(resp + ixstart);
                printf("%d\n", v);
            }
        }
    }
    {
        char req[1000];
        snprintf(req, sizeof(req), "<methodCall><methodName>%s</methodName><params></params></methodCall>", "Test.getString");
        struct vms_http *ctx = http_post(host, port, "/", "text/xml", "text/xml", req);
        char resp[10000];
        short resplen;
        http_recv_all(ctx, resp, sizeof(resp), &resplen);
        resp[resplen] = 0;
        http_close(ctx);
        int ixstart = 0;
        int ixend = strlen(resp) - 1;
        if(grab(resp, &ixstart, &ixend, "value", GREEDY))
        {
            if(grab(resp, &ixstart, &ixend, "string", GREEDY))
            {
                char s[1000];
                strncpy(s, resp + ixstart, ixend - ixstart);
                s[ixend - ixstart] = 0;
                printf("%s\n", s);
            }   
            else
            {
                char s[1000];
                strncpy(s, resp + ixstart, ixend - ixstart);
                s[ixend - ixstart] = 0;
                printf("%s\n", s);
            }
        }
    } 
    {
        char req[1000];
        snprintf(req, sizeof(req), "<methodCall><methodName>%s</methodName><params></params></methodCall>", "Test.getListOfInts");
        struct vms_http *ctx = http_post(host, port, "/", "text/xml", "text/xml", req);
        char resp[10000];
        short resplen;
        http_recv_all(ctx, resp, sizeof(resp), &resplen);
        resp[resplen] = 0;
        http_close(ctx);
        int ixstart = 0;
        int ixend = strlen(resp) - 1;
        if(grab(resp, &ixstart, &ixend, "value", GREEDY))
        {
            if(grab(resp, &ixstart, &ixend, "array", GREEDY))
            {
                int ixstart2 = ixstart;
                int ixend2 = 0;
                int a[100];
                int n = 0;
                while(grab(resp, &ixstart2, &ixend2, "value", NONGREEDY))
                {
                    int ixstart3 = ixstart2;
                    int ixend3 = ixend2;
                    if(grab(resp, &ixstart3, &ixend3, "i4", GREEDY))
                    {
                        a[n] = atoi(resp + ixstart3);
                        n++;
                    }
                    else if(grab(resp, &ixstart3, &ixend3, "int", GREEDY))
                    {
                        a[n] = atoi(resp + ixstart3);
                        n++;
                    }
                    ixstart2 = ixend2;
                    ixend2 = ixend;
                }
                printf("[");
                for(int i = 0; i < n; i++)
                {
                    if(i > 0) printf(",");
                    printf("%d", a[i]);
                }
                printf("]\n");
            }   
        }
    }
    {
        char req[1000];
        snprintf(req, sizeof(req), "<methodCall><methodName>%s</methodName><params></params></methodCall>", "Test.getListOfStrings");
        struct vms_http *ctx = http_post(host, port, "/", "text/xml", "text/xml", req);
        char resp[10000];
        short resplen;
        http_recv_all(ctx, resp, sizeof(resp), &resplen);
        resp[resplen] = 0;
        http_close(ctx);
        int ixstart = 0;
        int ixend = strlen(resp) - 1;
        if(grab(resp, &ixstart, &ixend, "value", GREEDY))
        {
            if(grab(resp, &ixstart, &ixend, "array", GREEDY))
            {
                int ixstart2 = ixstart;
                int ixend2 = 0;
                char a[100][100];
                int n = 0;
                while(grab(resp, &ixstart2, &ixend2, "value", NONGREEDY))
                {
                    int ixstart3 = ixstart2;
                    int ixend3 = ixend2;
                    if(grab(resp, &ixstart3, &ixend3, "string", GREEDY))
                    {
                        strncpy(a[n],  resp + ixstart3, ixend3 - ixstart3);
                        a[n][ixend3 - ixstart3] = 0;
                        n++;
                    }
                    else
                    {
                        strncpy(a[n],  resp + ixstart3, ixend3 - ixstart3);
                        a[n][ixend3 - ixstart3] = 0;
                        n++;
                    }
                    ixstart2 = ixend2;
                    ixend2 = ixend;
                }
                printf("[");
                for(int i = 0; i < n; i++)
                {
                    if(i > 0) printf(",");
                    printf("%s", a[i]);
                }
                printf("]\n");
            }   
        }
    }
    {
        char req[1000];
        int v1 = 123;
        int v2 = 456;
        snprintf(req, sizeof(req), "<methodCall><methodName>%s</methodName><params><param><value><i4>%d</i4></value></param><param><value><i4>%d</i4></value></param></params></methodCall>", "Test.add", v1, v2);
        struct vms_http *ctx = http_post(host, port, "/", "text/xml", "text/xml", req);
        char resp[10000];
        short resplen;
        http_recv_all(ctx, resp, sizeof(resp), &resplen);
        resp[resplen] = 0;
        http_close(ctx);
        int ixstart = 0;
        int ixend = strlen(resp) - 1;
        if(grab(resp, &ixstart, &ixend, "value", GREEDY))
        {
            if(grab(resp, &ixstart, &ixend, "i4", GREEDY))
            {
                int v3 = atoi(resp + ixstart);
                printf("%d\n", v3); 
            }
            else if(grab(resp, &ixstart, &ixend, "int", GREEDY))
            {
                int v3 = atoi(resp + ixstart);
                printf("%d\n", v3);
            }
        }
    }
    {
        char req[1000];
        char *s1 = "ABC";
        char *s2 = "DEF";
        snprintf(req, sizeof(req), "<methodCall><methodName>%s</methodName><params><param><value><string>%s</string></value></param><param><value><string>%s</string></value></param></params></methodCall>", "Test.concat", s1, s2);
        struct vms_http *ctx = http_post(host, port, "/", "text/xml", "text/xml", req);
        char resp[10000];
        short resplen;
        http_recv_all(ctx, resp, sizeof(resp), &resplen);
        resp[resplen] = 0;
        http_close(ctx);
        int ixstart = 0;
        int ixend = strlen(resp) - 1;
        if(grab(resp, &ixstart, &ixend, "value", GREEDY))
        {
            if(grab(resp, &ixstart, &ixend, "string", GREEDY))
            {
                char s3[1000];
                strncpy(s3, resp + ixstart, ixend - ixstart);
                s3[ixend - ixstart] = 0;
                printf("%s\n", s3);
            }   
            else
            {
                char s3[1000];
                strncpy(s3, resp + ixstart, ixend - ixstart);
                s3[ixend - ixstart] = 0;
                printf("%s\n", s3);
            }
        }
    } 
}

int main()
{
    test("localhost", 8001);
    test("localhost", 8002);
    return 0;
}
$ cc/incl=xmlrpcdir diyclient
$ link diyclient + xmlrpcdir:xmlrpc/lib
$ run diyclient

We see that the C code using direct XML is more complex than the C code using libxmlrpc (as shown in XML-RPC) and C code using my homegrown library (as shown in VMS Tech Demo 11 - XML-RPC).

Pascal:

[inherit('pxmlrpcdir:common', 'pxmlrpcdir:phttp')]
program diyclient(input,output);

function grab(xmlstr : pstr; var xstart : integer; var xend : integer; tag : pstr; greedy : boolean) : boolean;

var
   starttag, endtag : pstr;
   ixstart, ixend : integer;

begin
   starttag := '<' + tag + '>';
   endtag := '</' + tag + '>';
   ixstart := xstart;
   while (ixstart < (length(xmlstr) - length(starttag))) and (substr(xmlstr, ixstart, length(starttag)) <> starttag) do ixstart := ixstart + 1;
   ixstart := ixstart + length(starttag);
   if(greedy) then begin
      ixend := xend - length(endtag);
      while (ixend > 1) and (substr(xmlstr, ixend, length(endtag)) <> endtag) do ixend := ixend - 1;
   end else begin
      ixend := ixstart;
      while((ixend < (length(xmlstr) - length(starttag))) and (substr(xmlstr, ixend, length(endtag)) <> endtag)) do ixend := ixend + 1;
   end;
   if (ixstart < (length(xmlstr) - length(starttag))) and (ixend >= 1) then begin
       xstart := ixstart;
       xend := ixend;
       grab := true;
   end else begin
       grab := false;
   end;
end;

procedure test(host : pstr; port : integer);

procedure test_getInt(host : pstr; port : integer);

var
   req, resp : pstr;
   ctx : http;
   ixstart, ixend, v : integer;

begin
   req := '<methodCall><methodName>Test.getInt</methodName><params></params></methodCall>';
   ctx := http_post(fix(host), port, '/', 'text/xml', 'text/xml', fix(req));
   http_recv_all(ctx, resp.body, resp.length);
   ixstart := 1;
   ixend := length(resp);
   if grab(resp, ixstart, ixend, 'value', true) then begin
      if grab(resp, ixstart, ixend, 'i4', true) then begin
         readv(substr(resp, ixstart, ixend - ixstart), v);
         writeln(v:1);
      end else begin
         if grab(resp, ixstart, ixend, 'int', true) then begin
            readv(substr(resp, ixstart, ixend - ixstart), v);
            writeln(v:1);
         end;
      end;
   end;
end;

procedure test_getString(host : pstr; port : integer);

var
   req,  resp, s : pstr;
   ctx : http;
   ixstart, ixend : integer;

begin
   req := '<methodCall><methodName>Test.getString</methodName><params></params></methodCall>';
   ctx := http_post(fix(host), port, '/', 'text/xml', 'text/xml', fix(req));
   http_recv_all(ctx, resp.body, resp.length);
   ixstart := 1;
   ixend := length(resp);
   if grab(resp, ixstart, ixend, 'value', true) then begin
      if grab(resp, ixstart, ixend, 'string', true) then begin
         s := substr(resp, ixstart, ixend - ixstart);
         writeln(s);
      end else begin
         s := substr(resp, ixstart, ixend - ixstart);
         writeln(s);
      end;
   end;
end;

procedure test_getListOfInts(host : pstr; port : integer);

var
   req, resp : pstr;
   ctx : http;
   ixstart, ixend, ixstart2, ixend2, ixstart3, ixend3, n, i : integer;
   a : array [1..1000] of integer;

begin
   req := '<methodCall><methodName>Test.getListOfInts</methodName><params></params></methodCall>';
   ctx := http_post(fix(host), port, '/', 'text/xml', 'text/xml', fix(req));
   http_recv_all(ctx, resp.body, resp.length);
   ixstart := 1;
   ixend := length(resp);
   if grab(resp, ixstart, ixend, 'value', true) then begin
      if grab(resp, ixstart, ixend, 'array', true) then begin
         ixstart2 := ixstart;
         ixend2 := 0;
         n := 0;
         while grab(resp, ixstart2, ixend2, 'value', false) do begin
            ixstart3 := ixstart2;
            ixend3 := ixend2;
            if grab(resp, ixstart3, ixend3, 'i4', true) then begin
               n := n + 1;
               readv(substr(resp, ixstart3, ixend3 - ixstart3), a[n]);
            end else begin
               if grab(resp, ixstart3, ixend3, 'int', true) then begin
                  n := n + 1;
                  readv(substr(resp, ixstart3, ixend3 - ixstart3), a[n]);
               end;
            end;
            ixstart2 := ixend2;
            ixend2 := ixend;
         end;
         write('[');
         for i := 1 to n do begin
            if i > 1 then write(',');
            write(a[i]:1);
         end;
         writeln(']');
      end;
   end;
end;

procedure test_getListOfStrings(host : pstr; port : integer);

var
   req, resp : pstr;
   ctx : http;
   ixstart, ixend, ixstart2, ixend2, ixstart3, ixend3, n, i : integer;
   a : array [1..1000] of pstr;

begin
   req := '<methodCall><methodName>Test.getListOfStrings</methodName><params></params></methodCall>';
   ctx := http_post(fix(host), port, '/', 'text/xml', 'text/xml', fix(req));
   http_recv_all(ctx, resp.body, resp.length);
   ixstart := 1;
   ixend := length(resp);
   if grab(resp, ixstart, ixend, 'value', true) then begin
      if grab(resp, ixstart, ixend, 'array', true) then begin
         ixstart2 := ixstart;
         ixend2 := 0;
         n := 0;
         while grab(resp, ixstart2, ixend2, 'value', false) do begin
            ixstart3 := ixstart2;
            ixend3 := ixend2;
            if grab(resp, ixstart3, ixend3, 'string', true) then begin
               n := n + 1;
               a[n] := substr(resp, ixstart3, ixend3 - ixstart3);
            end else begin
               n := n + 1;
               a[n] := substr(resp, ixstart3, ixend3 - ixstart3);
            end;
            ixstart2 := ixend2;
            ixend2 := ixend;
         end;
         write('[');
         for i := 1 to n do begin
            if i > 1 then write(',');
            write(a[i]);
         end;
         writeln(']');
      end;
   end;
end;

procedure test_add(host : pstr; port : integer);

var
   req, resp : pstr;
   ctx : http;
   ixstart, ixend, v1, v2, v3 : integer;

begin
   v1 := 123;
   v2 := 456;
   req := '<methodCall><methodName>Test.add</methodName><params><param><value><i4>' + dec(v1) + '</i4></value></param><param><value><i4>' + dec(v2) + '</i4></value></param></params></methodCall>';
   ctx := http_post(fix(host), port, '/', 'text/xml', 'text/xml', fix(req));
   http_recv_all(ctx, resp.body, resp.length);
   ixstart := 1;
   ixend := length(resp);
   if grab(resp, ixstart, ixend, 'value', true) then begin
      if grab(resp, ixstart, ixend, 'i4', true) then begin
         readv(substr(resp, ixstart, ixend - ixstart), v3);
         writeln(v3:1);
      end else begin
         if grab(resp, ixstart, ixend, 'int', true) then begin
            readv(substr(resp, ixstart, ixend - ixstart), v3);
            writeln(v3:1);
         end;
      end;
   end;
end;

procedure test_concat(host : pstr; port : integer);

var
   req, resp : pstr;
   ctx : http;
   ixstart, ixend : integer;
   s1, s2, s3 : pstr;

begin
   s1 := 'ABC';
   s2 := 'DEF';
   req := '<methodCall><methodName>Test.concat</methodName><params><param><value><string>' + s1 + '</string></value></param><param><value><string>' + s2 + '</string></value></param></params></methodCall>';
   ctx := http_post(fix(host), port, '/', 'text/xml', 'text/xml', fix(req));
   http_recv_all(ctx, resp.body, resp.length);
   ixstart := 1;
   ixend := length(resp);
   if grab(resp, ixstart, ixend, 'value', true) then begin
      if grab(resp, ixstart, ixend, 'string', true) then begin
         s3 := substr(resp, ixstart, ixend - ixstart);
         writeln(s3);
      end else begin
         s3 := substr(resp, ixstart, ixend - ixstart);
         writeln(s3);
      end;
   end;
end;

begin
   writeln('http://', host, ':', port:1, '/ (Pascal)');
   test_getInt(host, port);
   test_getString(host, port);
   test_getListOfInts(host, port);
   test_getListOfStrings(host, port);
   test_add(host, port);
   test_concat(host, port);
end;

begin
   test('localhost', 8001);
   test('localhost', 8002);
end.
$ pas diyclient
$ link diyclient + pxmlrpcdir:pxmlrpc/lib + xmlrpcdir:xmlrpc/lib
$ run diyclient

We see that the Pascal code using direct XML is more complex than the Pascal code using my homegrown library (as shown in VMS Tech Demo 11 - XML-RPC).

Fortran:

      program diyclient
      call test('localhost', 8001)
      call test('localhost', 8002)
      end program
c
      subroutine test(host, port)
      character*(*) host
      integer*4 port
      write(*,100)  host, port
      call test1(host, port)
      call test2(host, port)
      call test3(host, port)
      call test4(host, port)
      call test5(host, port)
      call test6(host, port)
100   format(1x,'http://',a,':',i4,'/ (Fortran)')
      end
c
      subroutine test1(host, port)
      character*(*) host
      integer*4 port
      character*1000 req, resp
      integer*4 ctx, resplen, n, ixstart, ixend, v
      integer*4 vms_http_post
      integer*4 vms_http_recv_all
      logical*4 grab
      req = '<methodCall>'//
     +      '<methodName>Test.getInt</methodName>'//
     +      '<params></params>'//
     +      '</methodCall>'
      ctx = vms_http_post(host, port, '/', 'text/xml', 'text/xml', req)
      n = vms_http_recv_all(ctx, resp, resplen)
      call vms_http_close(ctx)
      ixstart = 1
      ixend = resplen
      if(grab(resp(1:resplen), ixstart, ixend, 'value',.true.)) then
        if(grab(resp(1:resplen), ixstart, ixend, 'i4',.true.)) then
          read(resp(ixstart:ixend-1), '(i)') v
          write(*,*) v
        else if(grab(resp(1:resplen), ixstart, ixend, 'int',.true.))then
          read(resp(ixstart:ixend-1), '(i)') v
          write(*,*) v
        endif
      endif
      end
c
      subroutine test2(host, port)
      character*(*) host
      integer*4 port
      character*1000 req, resp
      integer*4 ctx, resplen, n, ixstart, ixend, slen
      character*100 s
      integer*4 vms_http_post
      integer*4 vms_http_recv_all
      logical*4 grab
      req = '<methodCall>'//
     +      '<methodName>Test.getString</methodName>'//
     +      '<params></params>'//
     +      '</methodCall>'
      ctx = vms_http_post(host, port, '/', 'text/xml', 'text/xml', req)
      n = vms_http_recv_all(ctx, resp, resplen)
      call vms_http_close(ctx)
      ixstart = 1
      ixend = resplen
      if(grab(resp(1:resplen), ixstart, ixend, 'value',.true.)) then
        if(grab(resp(1:resplen), ixstart, ixend, 'string',.true.)) then
          slen = ixend - ixstart
          s(1:slen) = resp(ixstart:ixend-1)
          write(*,*) s(1:slen)
        else
          slen = ixend - ixstart
          s(1:slen) = resp(ixstart:ixend-1)
          write(*,*) s(1:slen)
        endif
      endif
      end
c
      subroutine test3(host, port)
      character*(*) host
      integer*4 port
      character*1000 req, resp
      integer*4 ctx, resplen, n, ixstart, ixend,
     +          ixstart2, ixend2, ixstart3, ixend3, i
      integer*4 a(1000)
      integer*4 vms_http_post
      integer*4 vms_http_recv_all
      logical*4 grab
      req = '<methodCall>'//
     +      '<methodName>Test.getListOfInts</methodName>'//
     +      '<params></params>'//
     +      '</methodCall>'
      ctx = vms_http_post(host, port, '/', 'text/xml', 'text/xml', req)
      n = vms_http_recv_all(ctx, resp, resplen)
      call vms_http_close(ctx)
      ixstart = 1
      ixend = resplen
      if(grab(resp(1:resplen), ixstart, ixend, 'value', .true.)) then
        if(grab(resp(1:resplen), ixstart, ixend, 'array', .true.)) then
          ixstart2 = ixstart
          ixend2 = 0
          n = 0
100       if(grab(resp(1:resplen), ixstart2, ixend2,
     +            'value', .false.)) then
            ixstart3 = ixstart2
            ixend3 = ixend2
            if(grab(resp(1:resplen), ixstart3, ixend3,
     +              'i4', .true.)) then
              n = n + 1
              read(resp(ixstart3:ixend3-1), '(i)') a(n)
            else if(grab(resp(1:resplen), ixstart3, ixend3,
     +                   'int', .true.)) then
                n = n + 1
                read(resp(ixstart3:ixend3-1), '(i)') a(n)
            endif
            ixstart2 = ixend2
            ixend2 = ixend
            goto 100
          endif
          write(*,'(1x,1h[,i,<n-1>(1h,,i),1h])') (a(i),i=1,n)
        endif
      endif
      end
c
      subroutine test4(host, port)
      character*(*) host
      integer*4 port
      character*1000 req, resp
      integer*4 ctx, resplen, n, ixstart, ixend, v
      character*100 a(1000)
      integer*4 alen(1000)
      integer*4 vms_http_post
      integer*4 vms_http_recv_all
      logical*4 grab
      req = '<methodCall>'//
     +      '<methodName>Test.getListOfStrings</methodName>'//
     +      '<params></params>'//
     +      '</methodCall>'
      ctx = vms_http_post(host, port, '/', 'text/xml', 'text/xml', req)
      n = vms_http_recv_all(ctx, resp, resplen)
      call vms_http_close(ctx)
      ixstart = 1
      ixend = resplen
      if(grab(resp(1:resplen), ixstart, ixend, 'value', .true.)) then
        if(grab(resp(1:resplen), ixstart, ixend, 'array', .true.)) then
          ixstart2 = ixstart
          ixend2 = 0
          n = 0
100       if(grab(resp(1:resplen), ixstart2, ixend2,
     +            'value', .false.)) then
            ixstart3 = ixstart2
            ixend3 = ixend2
            if(grab(resp(1:resplen), ixstart3, ixend3,
     +              'string', .true.)) then
              n = n + 1
              alen(n) = ixend3 - ixstart3
              a(n) = resp(ixstart3:ixend3-1)
            else
              n = n + 1
              alen(n) = ixend3 - ixstart3
              a(n) = resp(ixstart3:ixend3-1)
            endif
            ixstart2 = ixend2
            ixend2 = ixend
            goto 100
          endif
          write(*,'(1x,1h[,a,<n-1>(1h,,a),1h])') (a(i)(1:alen(i)),i=1,n)
        endif
      endif
      end
c
      subroutine test5(host, port)
      character*(*) host
      integer*4 port
      character*1000 req, resp
      integer*4 ctx, resplen, n, ixstart, ixend, v1, v2, v3
      character*10 sv1, sv2
      integer*4 vms_http_post
      integer*4 vms_http_recv_all
      logical*4 grab
      v1 = 123
      v2 = 456
      write(sv1, '(i10)') v1
      write(sv2, '(i10)') v2
      req = '<methodCall>'//
     +      '<methodName>Test.add</methodName>'//
     +      '<params>'//
     +      '<param><value><i4>'//sv1//'</i4></value></param>'//
     +      '<param><value><i4>'//sv2//'</i4></value></param>'//
     +      '</params>'//
     +      '</methodCall>'
      ctx = vms_http_post(host, port, '/', 'text/xml', 'text/xml', req)
      n = vms_http_recv_all(ctx, resp, resplen)
      call vms_http_close(ctx)
      ixstart = 1
      ixend = resplen
      if(grab(resp(1:resplen), ixstart, ixend, 'value',.true.)) then
        if(grab(resp(1:resplen), ixstart, ixend, 'i4',.true.)) then
          read(resp(ixstart:ixend-1), '(i)') v3
          write(*,*) v3
        else if(grab(resp(1:resplen), ixstart, ixend, 'int',.true.))then
          read(resp(ixstart:ixend-1), '(i)') v3
          write(*,*) v3
        endif
      endif
      end
c
      subroutine test6(host, port)
      character*(*) host
      integer*4 port
      character*1000 req, resp
      integer*4 ctx, resplen, n, ixstart, ixend, s3len
      character*100 s1, s2, s3
      integer*4 vms_http_post
      integer*4 vms_http_recv_all
      logical*4 grab
      s1 = 'ABC'
      s2 = 'DEF'
      req = '<methodCall>'//
     +      '<methodName>Test.concat</methodName>'//
     +      '<params>'//
     +      '<param><value><string>'//
     +      trim(s1)//
     +      '</string></value></param>'//
     +      '<param><value><string>'//
     +      trim(s2)//
     +      '</string></value></param>'//
     +      '</params>'//
     +      '</methodCall>'
      ctx = vms_http_post(host, port, '/', 'text/xml', 'text/xml', req)
      n = vms_http_recv_all(ctx, resp, resplen)
      call vms_http_close(ctx)
      ixstart = 1
      ixend = resplen
      if(grab(resp(1:resplen), ixstart, ixend, 'value',.true.)) then
        if(grab(resp(1:resplen), ixstart, ixend, 'string',.true.)) then
          s3len = ixend - ixstart
          s3(1:s3len) = resp(ixstart:ixend-1)
          write(*,*) s3(1:s3len)
        else
          s3len = ixend - ixstart
          s3(1:s3len) = resp(ixstart:ixend-1)
          write(*,*) s3(1:s3len)
        endif
      endif
      end
c
      logical*4 function grab(xmlstr, xstart, xend, tag, greedy)
      character*(*) xmlstr, tag
      integer*4 xstart, xend
      logical*4 greedy
      character*100 starttag, endtag
      integer*4 starttaglen, endtaglen, ixstart, ixend
      starttaglen = 1 + len(tag) + 1
      starttag(1:starttaglen) = '<'//tag//'>'
      endtaglen = 2 + len(tag) + 1
      endtag(1:endtaglen) = '</'//tag//'>'
      ixstart = xstart
100   if((ixstart.lt.(len(xmlstr)-starttaglen)).and.
     +   (xmlstr(ixstart:ixstart+starttaglen-1).ne.
     +   starttag(1:starttaglen))) then
        ixstart = ixstart + 1
        goto 100
      endif
      ixstart = ixstart + starttaglen
      if(greedy) then
        ixend = xend - endtaglen
200     if((ixend.gt.1).and.
     +     (xmlstr(ixend:ixend+endtaglen-1).ne.endtag(1:endtaglen)))then
          ixend = ixend - 1
          goto 200
        endif
      else
        ixend = ixstart
300     if((ixend.lt.(len(xmlstr)-starttaglen)).and.
     +     (xmlstr(ixend:ixend+endtaglen-1).ne.endtag(1:endtaglen)))then
          ixend = ixend + 1
          goto 300
        endif
      endif
      if((ixstart.lt.(len(xmlstr)-starttaglen)).and.(ixend.ge.1)) then
        xstart = ixstart
        xend = ixend
        grab = .true.
      else
        grab = .false.
      endif
      end
$ for diyclient
$ link diyclient + pxmlrpcdir:pxmlrpc/lib + xmlrpcdir:xmlrpc/lib
$ run diyclient

We see that Fortran (77) is not a good language for XML-RPC programming. The string handling is too primitive.

Basic:

program diyclient

external sub test(string, integer)

call test("localhost", 8001)
call test("localhost", 8002)

end program
!
sub test(string host, integer port)

external sub test1(string, integer)
external sub test2(string, integer)

print "http://" + host + ":" + str$(port) + "/ (Basic)"
call test1(host, port)
call test2(host, port)
call test3(host, port)
call test4(host, port)
call test5(host, port)
call test6(host, port)

end sub
!
sub test1(string host, integer port)

declare string req
map (resp) string resp = 1000
declare integer ctx, resplen, n, ixstart, ixend, v

external integer function vms_http_post(string, integer, string, string, string, string)
external integer function vms_http_recv_all(integer, string, integer)
external sub vms_http_close(integer)
external integer function grab(string, integer, integer, string, integer)

req = "<methodCall><methodName>Test.getInt</methodName><params></params></methodCall>"
ctx = vms_http_post(host, port, "/", "text/xml", "text/xml", req)
n = vms_http_recv_all(ctx, resp, resplen)
call vms_http_close(ctx)
ixstart = 1
ixend = len(resp)
if grab(mid$(resp, 1, resplen), ixstart, ixend, "value", 1) <> 0 then
    if grab(mid$(resp, 1, resplen), ixstart, ixend, "i4", 1) <> 0 then
        v = integer(mid$(resp, ixstart, ixend - ixstart))
        print v
    else
        if grab(mid$(resp, 1, resplen), ixstart, ixend, "int", 1) <> 0 then
            v = integer(mid$(resp, ixstart, ixend - ixstart))
            print v
        end if
    end if
end if

end sub
!
sub test2(string host, integer port)

declare string req, s
map (resp) string resp = 1000
declare integer ctx, resplen, n, ixstart, ixend

external integer function vms_http_post(string, integer, string, string, string, string)
external integer function vms_http_recv_all(integer, string, integer)
external sub vms_http_close(integer)
external integer function grab(string, integer, integer, string, integer)

req = "<methodCall><methodName>Test.getString</methodName><params></params></methodCall>"
ctx = vms_http_post(host, port, "/", "text/xml", "text/xml", req)
n = vms_http_recv_all(ctx, resp, resplen)
call vms_http_close(ctx)
ixstart = 1
ixend = len(resp)
if grab(mid$(resp, 1, resplen), ixstart, ixend, "value", 1) <> 0 then
    if grab(mid$(resp, 1, resplen), ixstart, ixend, "string", 1) <> 0 then
        s = mid$(resp, ixstart, ixend - ixstart)
        print s
    else
        s = mid$(resp, ixstart, ixend - ixstart)
        print s
    end if
end if

end sub
!
sub test3(string host, integer port)

declare string req, prtlin
map (resp) string resp = 1000
declare integer ctx, resplen, n, ixstart, ixend, ixstart2, ixend2, ixstart3, ixend3
declare integer a(1000)

external integer function vms_http_post(string, integer, string, string, string, string)
external integer function vms_http_recv_all(integer, string, integer)
external sub vms_http_close(integer)
external integer function grab(string, integer, integer, string, integer)

req = "<methodCall><methodName>Test.getListOfInts</methodName><params></params></methodCall>"
ctx = vms_http_post(host, port, "/", "text/xml", "text/xml", req)
n = vms_http_recv_all(ctx, resp, resplen)
call vms_http_close(ctx)
ixstart = 1
ixend = len(resp)
if grab(mid$(resp, 1, resplen), ixstart, ixend, "value", 1) then
    if grab(mid$(resp, 1, resplen), ixstart, ixend, "array", 1) then
        ixstart2 = ixstart
        ixend2 = 0
        n = 0
        while grab(mid$(resp, 1, resplen), ixstart2, ixend2, "value", 0)
            ixstart3 = ixstart2
            ixend3 = ixend2
            if grab(mid$(resp, 1, resplen), ixstart3, ixend3, "i4", 1) then
                n = n + 1
                a(n) = integer(mid$(resp, ixstart3, ixend3 - ixstart3))
            else
                if grab(mid$(resp, 1, resplen), ixstart3, ixend3, "int", 1) then
                    n = n + 1
                    a(n) = integer(mid$(resp, ixstart3, ixend3 - ixstart3))
                end if
            end if
            ixstart2 = ixend2
            ixend2 = ixend
        next
        prtlin = "["
        for i = 1 to n
            if i > 1 then
                prtlin = prtlin + ","
            end if
            prtlin = prtlin + str$(a(i))
        next i
        prtlin = prtlin + "]"
        print prtlin
    end if
end if

end sub
!
sub test4(string host, integer port)

declare string req, prtlin
map (resp) string resp = 1000
declare integer ctx, resplen, n, ixstart, ixend, ixstart2, ixend2, ixstart3, ixend3
declare string a(1000)

external integer function vms_http_post(string, integer, string, string, string, string)
external integer function vms_http_recv_all(integer, string, integer)
external sub vms_http_close(integer)
external integer function grab(string, integer, integer, string, integer)

req = "<methodCall><methodName>Test.getListOfStrings</methodName><params></params></methodCall>"
ctx = vms_http_post(host, port, "/", "text/xml", "text/xml", req)
n = vms_http_recv_all(ctx, resp, resplen)
call vms_http_close(ctx)
ixstart = 1
ixend = len(resp)
if grab(mid$(resp, 1, resplen), ixstart, ixend, "value", 1) then
    if grab(mid$(resp, 1, resplen), ixstart, ixend, "array", 1) then
        ixstart2 = ixstart
        ixend2 = 0
        n = 0
        while grab(mid$(resp, 1, resplen), ixstart2, ixend2, "value", 0)
            ixstart3 = ixstart2
            ixend3 = ixend2
            if grab(mid$(resp, 1, resplen), ixstart3, ixend3, "string", 1) then
                n = n + 1
                a(n) = mid$(resp, ixstart3, ixend3 - ixstart3)
            else
                n = n + 1
                a(n) = mid$(resp, ixstart3, ixend3 - ixstart3)
            end if
            ixstart2 = ixend2
            ixend2 = ixend
        next
        prtlin = "["
        for i = 1 to n
            if i > 1 then
                prtlin = prtlin + ","
            end if
            prtlin = prtlin + a(i)
        next i
        prtlin = prtlin + "]"
        print prtlin
    end if
end if

end sub
!
sub test5(string host, integer port)

declare string req
map (resp) string resp = 1000
declare integer ctx, resplen, n, ixstart, ixend, v1, v2, v3

external integer function vms_http_post(string, integer, string, string, string, string)
external integer function vms_http_recv_all(integer, string, integer)
external sub vms_http_close(integer)
external integer function grab(string, integer, integer, string, integer)

v1 = 123
v2 = 456
req = "<methodCall><methodName>Test.add</methodName><params><param><value><i4>" + str$(v1) + "</i4></value></param><param><value><i4>" + str$(v2) + "</i4></value></param></params></methodCall>"
ctx = vms_http_post(host, port, "/", "text/xml", "text/xml", req)
n = vms_http_recv_all(ctx, resp, resplen)
call vms_http_close(ctx)
ixstart = 1
ixend = len(resp)
if grab(mid$(resp, 1, resplen), ixstart, ixend, "value", 1) <> 0 then
    if grab(mid$(resp, 1, resplen), ixstart, ixend, "i4", 1) <> 0 then
        v3 = integer(mid$(resp, ixstart, ixend - ixstart))
        print v3
    else
        if grab(mid$(resp, 1, resplen), ixstart, ixend, "int", 1) <> 0 then
            v3 = integer(mid$(resp, ixstart, ixend - ixstart))
            print v3
        end if
    end if
end if

end sub
!
sub test6(string host, integer port)

declare string req, s1, s2, s3
map (resp) string resp = 1000
declare integer ctx, resplen, n, ixstart, ixend

external integer function vms_http_post(string, integer, string, string, string, string)
external integer function vms_http_recv_all(integer, string, integer)
external sub vms_http_close(integer)
external integer function grab(string, integer, integer, string, integer)

s1 = "ABC"
s2 = "DEF"
req = "<methodCall><methodName>Test.concat</methodName><params><param><value><string>" + s1 + "</string></value></param><param><value><string>" + s2 + "</string></value></param></params></methodCall>"
ctx = vms_http_post(host, port, "/", "text/xml", "text/xml", req)
n = vms_http_recv_all(ctx, resp, resplen)
call vms_http_close(ctx)
ixstart = 1
ixend = len(resp)
if grab(mid$(resp, 1, resplen), ixstart, ixend, "value", 1) <> 0 then
    if grab(mid$(resp, 1, resplen), ixstart, ixend, "string", 1) <> 0 then
        s3 = mid$(resp, ixstart, ixend - ixstart)
        print s3
    else
        s3 = mid$(resp, ixstart, ixend - ixstart)
        print s3
    end if
end if

end sub
!
function integer grab(string xmlstr, integer xstart, integer xend, string tag, integer greedy)

declare string starttag, endtag
declare integer ixstart, ixend

starttag = "<" + tag + ">"
endtag = "</" + tag + ">"
ixstart = xstart
while (ixstart < (len(xmlstr) - len(starttag))) and (mid$(xmlstr, ixstart, len(starttag))) <> starttag
    ixstart = ixstart + 1
next
ixstart = ixstart + len(starttag)
if greedy <> 0 then
    ixend = xend - len(endtag)
    while (ixend > 1) and (mid$(xmlstr, ixend, len(endtag)) <> endtag)
        ixend = ixend - 1
    next
else
    ixend = ixstart
    while (ixend < (len(xmlstr) - len(starttag))) and (mid$(xmlstr, ixend, len(endtag)) <> endtag)
        ixend = ixend + 1
    next
end if
if (ixstart < len(xmlstr) - len(starttag)) and (ixend >= 1) then
    xstart = ixstart
    xend = ixend
    grab = 1
else
    grab = 0
end if

end function
$ bas diyclient
$ link diyclient + xmlrpcdir:xmlrpc/lib
$ run diyclient

Basic is as good as Pascal.

Article history:

Version Date Description
1.0 February 11th 2024 Initial version

Other articles:

See list of all articles here

Comments:

Please send comments to Arne Vajhøj