Creating a Client Interface for a MANASYS Jazz Web Service

This is a work-in-progress
See Road Map for its current status, and proposed future development

Contents

Creating a Client Interface for a MANASYS Jazz Web Service. 1

Introduction. 1

Prerequisites. 1

Revision: Creating a JSON Web Service. 1

Configuration and Set Up. 1

Step 1.  Create an Interface Project 1

Step 2 (Optional).  Create Projects for Test Objects. 1

Step 3.  Configure Jazz – Client Tab. 1

Step 4.  Configure Jazz – Workbench Tab. 1

Creating a Client Interface. 1

Step 1.  Locating JSON and Creating the Message Descriptions. 1

Step2.  Creating the Client Interface. 1

Server-side and Client-Side Validation. 1

When do you Need to Re-create a Client Interface?. 1

To Re-create a Client Interface. 1

Using a Client Interface. 1

Getting Started – Locate or Create a Suitable Project 1

Creating More Forms. 1

Initializing the form.. 1

Creating a Client Form.. 1

Intellisense. 1

Source Code. 1

Form Logic – Methods. 1

Managing Scrolling. 1

Update, Delete, and Add. 1

Clear and Close. 1

Creating Similar Tests. 1

Client Projects with Multiple Forms. 1

Parent-Child Services. 1

Introduction: Test Data for JSPG3. 1

Creating Web Service JSPG3. 1

Creating and testing JSPG3Client 1

Sample Clients. 1

Appendices. 1

Programs used as Examples. 1

MANASYS Jazz programs. 1

Client Interfaces. 1

Test Programs. 1

Returning Multiple Records. 1

Logic used when Max > 1. 1

Measuring Response Time. 1

Browsecount 1

Jazz Message Definitions. 1

Message and Field Names. 1

Messages – COBOL, JSON, C#, and Binding. 1

Request message as JSON. 1

RequestJSPG2.cs. 1

ResponseJSPG2.cs. 1

Response JSPG2A.. 1

Understanding the Client Interface. 1

1.     Private. 1

2.     Key Properties. 1

3.     I/O Data. 1

4.     Output 1

5.     Value properties. 1

3.     Functions. 1

4.     Standard Response Properties. 1

5.     RequestResponse. 1

6.     Methods. 1

7.     Routines. 1

Road Map. 1

 

Introduction

In minutes MANASYS Jazz can generate a CICS Web Service that can perform enquiry and/or update from one or several VSAM files or DB2 database tables, generating hundreds of lines of COBOL logic and also the necessary supporting objects (WSDL or JSON, and the binding objects that convert between JSON or WSDL and COBOL).  The COBOL logic will validate incoming data and implement the rules of the web service such as “Update must follow an enquiry”. 

Both REST (JSON) and SOAP (WSDL) services can immediately be tested with utilities like PostMan and ReadyAPI, but even though a ReadyAPI test can be created in minutes it was far from easy to develop a client-side program to interact with a CICS Web Service, especially if you want client-side validation to avoid unnecessary web service requests.  However MANASYS Jazz can now generate an interface object for JSON web services that makes client development very easy.  Click here to see a short video introducing this process.   The interface exposes properties and methods encapsulating the rules of the service:  for example Employee.Sex must be M or F, Employee.Empno must be numeric, Update must follow an Enquiry and must return the CheckSum value from the Enquiry, and so on.  Clients need only refer to the properties and methods provided by the interface object to obey the rules.  Agile development is now easy: should the web service change, just re-generate the interface object to reflect new rules and data.  The Client Program (which you write) will need changes only to reflect new functions and data provided by the new version of the service.

In the diagram above, Jazz will have generated the service MyJSv-JSPG2 as a COBOL program on the “mainframe”, and the interface JSPG2Client as a C# object in your MyJSv interfaces project.  Why C#?  By targeting C# and .NET Core the interface can be used not only in Windows, but also with Linux, Android, IOS, and perhaps other operating systems.  You will write the actual client-side logic in the language of your choice – VB.NET, C#, Java, … for whatever client environment you are using (Windows form, Web page, mobile app…).

As later programs are generated their interfaces are added into the interface project for the service: -

This Users’ Guide chapter shows you how to create these client-side interfaces.

Note: this chapter uses various programs within Service MyJSv for its examples.  Programs have names starting with “JS”, so when you see “JSPG2” or “MyJSv” written, including as parts of longer words, mentally substitute your own names.  For a full list of the programs that might have been used in demonstrations, see Programs used as Examples.

Prerequisites

Web Service Clients like Interface JSPG2Client in the diagram above are delivered as C# projects. You need to have Visual Studio 2019 16.4 or later, and .Net Core 3.1 SDK or later.

You need to be using MANASYS Jazz version 3.16.1.239 or later.

This facility is only available for JSON (=REST) web services.  It is not available for WSDL (=SOAP) services.  REST now seems dominant, and so equivalent SOAP facilities are unlikely to be developed unless requested and funded.

Revision: Creating a JSON Web Service

Jazz web service programs like JSPG1 and JSPG2 can be quickly generated from a Jazz-format definition data definition.  In this example we’re going to create program JSPG2 to update the Employee table from IBM’s DB2 Sample database.  This definition was created from the New/Data/Import from SQL (select table EMPLOYEE) and then editing the definition to add extra information such as validity rules.  Here is the definition Employee.jzc, with highlighting to show my editing changes: -

*# Last Updated by JAZZUSR at 31/08/2020 1:33:28 PM

*# Created from Table:EMPLOYEE, Schema:ROBERTBW10, Database:Sample by JazzUser at 26/03/2019 4:25:46 PM

COPY Department;

DEFINE EMPLOYEE SQL DATA(   

    EMPNO CHAR(6) PIC '999999' REQUIRED KEY,

    FIRSTNME VARCHAR(12) REQUIRED,

    MIDINIT CHAR (1),

    LASTNAME VARCHAR(15) REQUIRED,

    WORKDEPT CHAR(3) CAPS DKEY 'XEMP2' EXISTS DEPARTMENT.DEPTNO,

    PHONENO CHAR(4) PIC '9999',

    HIREDATE DATE,

    JOB CHAR(8),

    EDLEVEL SMALLINT REQUIRED,

    SEX CHAR(1) CAPS CODES(M:Male, F:Female),

    BIRTHDATE DATE,

    SALARY MONEY(9,2) MIN 0,

    BONUS MONEY(9,2) MIN 0,

    COMM MONEY(9,2) MIN 0,

    CURRENCY CHAR(3),

    DEPTMGR BOOLEAN);

Editing changes were: -

·         To SQL EMPNO and PHONENO were simply CHAR fields, but they were supposed to be character-format positive integers.  SQL doesn’t support a PICTURE data type, but Jazz allows PIC to be given as a property.  Now any attempt to assign a value other than 000000 to 999999 to EMPNO will be detected and reported by ACCEPT.

·         WORKDEPT is a foreign key.  EXISTS ensures that non-null values must be current values of the primary key of DEPARTMENT.  So that the reference DEPARTMENT.DEPTNO is recognized correctly COPY Department; is added before DEFINE Employee … 
By also adding CAPS MANASYS ensures that values received as (for example) “d11” will be converted to “D11”.  We strongly recommend that any KEY, DKEY, or UKEY field use CAPS to avoid errors and ambiguity.

·         SEX can have any value as far as SQL is concerned, but the actual data rules are that it must be M meaning Male, or F meaning Female.  Adding CODES(M:Male, F:Female) specifies this.  As with a key value, CAPS is recommended.

·         Import from SQL rendered SALARY, BONUS, and COMM as DECIMAL.  Changing DECIMAL to MONEY will display them with $, and MIN 0 ensures that they can’t be -ve.

·         I added CURRENCY and DEPTMGR to IBM’s definition of the EMPLOYEE table.  CURRENCY was added to test that MANASYS Jazz could handle COBOL reserved words as SQL column names, and DEPTMGR was added to test that a typical “COBOL Boolean value” – a CHAR(1) field with possible values ‘Y’ or ‘N’ – would be handled correctly as a C# Boolean field with values True or False.

With Employee.jzc properly defined, the dialog New/Logic/Web service, with values like this, creates program JSPG2A to update the Employee table: - 

This dialog creates a Jazz definition of the request and response messages, and of the logic of the program (in this case a single-table update program with CICS-style pseudo-locking).    Here is the message definition for JSPG2A.   Here is a copy of program JSPG2A as Jazz, and as COBOL.

A web service request/response is a relatively slow process, in my test environment taking about 3 seconds whatever the size of the web service message, so it makes sense to return as much as possible each time.  Employee is defined with a duplicate key (WorkDept), and with my test data there were 14 records with WorkDept = D11, so JSPG2A was generated like JSPG2 except that I set Max >1.  If this were a production situation, I’d set max to 14 or greater.  For my testing I set Max=6 to test that the generated logic handled scrolling forward and back, automatically getting the next 6 (and the next) as you scrolled forward.  The takeaway: set Max as large as necessary, but it doesn’t have to be the maximum possible.  See the appendix Returning Multiple Records for more information.

The process is completed by compiling the COBOL, and generating the binding objects: the JSON description of the input and output messages, and WSBIND to convert these from and to COBOL.  With Jazz configured for zOS this happens automatically as JCL is created and submitted to compile the COBOL and then perform further steps to create the binding objects.  With Jazz configured for Micro Focus Enterprise Developer (MFED) there are a series of dialog steps to achieve the same effect.  This is covered elsewhere: see here for zOS, and here for MFED

Statistics so far:  Employee.jzc is 18 lines of code, 9 of which we edited.  From this we generated 54 lines of Jazz message definition (editable, but we didn’t in this case), and 28 lines of Jazz program logic (which again, we didn’t edit). This became 2500 lines of COBOL, and the ZOS or MFED process created 320 lines of JSON and the binding objects to convert between COBOL and JSON.  All of this compiles cleanly first time, and we have a running web service that can be tested with utilities such as ReadyAPI.

Now to get back to the real point of this Users’ Guide chapter, which is creating a client interface to make using such web services easy.  First we must configure MANASYS Jazz to create Client Interfaces.

Configuration and Set Up

We must first create an empty ASP.NET Core Web Application.  Then we will configure Jazz so it knows where to find this, so that it can create and update objects within it.

Step 1.  Create an Interface Project

1.    Open Visual Studio, create a new project. =>

2.    Select template ASP.NET Core Web Application, click [Next] =>

3.    Name the project MyJSv (or your web service name, see above).   
Note the location (e.g. copy it to the clipboard).  For me, this was C:\Users\Robertbw10\source\repos
Check “Place solution and project in the same directory”. Click [Create] =>

4.    Choose a template.  I’ve chosen Empty.  Click [Create] =>

Visual Studio creates the project and opens on an initial screen.  Solution explorer shows

 

5.    The generated interface is going to include the line
using Newtonsoft.Json;

To make this valid in project MyJSv, click menu Tools > Nuget Package Manager > Manage NuGet Packages for Solution.

Select the Browse Tab.   If Newtonsoft.Json is not already shown in this tab, enter Newtonsoft.json and click search

Click this.  It appears to the right. 

Click [  ] Project and then Install.   Click [OK] when asked.

Step 2 (Optional).  Create Projects for Test Objects

This is a planned feature, it is not implemented yet.

As well as the interface itself (the middle object in the diagram above) it is intended that Jazz will create template objects that will use the interface objects if you check the relevant Web or Windows checkbox (the left object in the diagram).  These template objects are basic Windows or Web programs that invoke the interface to use the web service: they are unlikely to appear or do exactly what you want, but they will have some code that you can adapt.  Template objects are usually created into separate projects, with default names MyJSvCS, MyJSvVB, MyJSvJV, etc.   C# objects can be created directly into the interface project.

If you are going to use this feature, then you need to identify the target projects before Jazz generates objects into them.  If necessary, create new VB and Java projects with appropriate templates.  Add a reference to the MyJSv project to these projects.

Step 3.  Configure Jazz – Client Tab

Click [Configure], click the Client tab. Identify the full path to the project file created above where the MyJSv (or your name) project has been created.  For example, C:\Users\Robertbw10\source\repos\MyJSv.

If you want template objects check the relevant checkboxes, and set or locate the relevant project (not necessary for C#).  Close the Configure form.

Step 4.  Configure Jazz – Workbench Tab

Interface generation uses a number of C# templates, with names @xxxx.cs.   If you have installed MANASYS Jazz before the Client Interface feature was developed then these objects will not have been included in your previous downloads.  If so, or you have been advised by your MANASYS support that there is an updated version, you should ensure that you have the latest version.

 

With WebAPI defined, return to the Workbench tab, and click [Initialize Project].   Select all the @xxxx.cs objects.   The current list is: -

@Add.cs

@BoolValue.cs

@Child.cs

@DateValue.cs

@Delete.cs

@InList.cs

@IsBool.cs

@IsDate.cs
@IsFloat.cs
@IsInt.cs
@IsNoLongerThan.cs
@JazzClient.cs
@Method.cs

@PChild.cs

@ReadNoScroll.cs

@ReadScroll.cs

@ReadScrollChild.cs

@Read

@RequestResponse.cs
@Update.cs

With the objects selected, click [Open].  This will copy them to your programs folder, so that they can be copied to the interface project created in Step 1. 

Creating a Client Interface

To create an interface, open the web service program for which you want to create an interface.  If it is a JSON web service, the button after [Configure] will be enabled and be labelled [Client].  You will need to have completed the development of the web service, not just generating COBOL but also compiling the COBOL and creating the Binding File and JSON message descriptions.  Click [Process] to create the latest version of your program, then depending on your test environment: -

·         zOS:  [Process] submits the job for remote compilation.  For a web service the job includes post-compilation steps to generate the JSON definitions and the Binding file (a program which converts between COBOL ó JSON Layouts).  Copy the JSON layouts back to your local (Windows) environment, into the root folder of your COBOL project.  For example, I’m using a folder structure C:Tutorials\TSTSQL that contains subfolders \Jazz Programs etc. For a program named JSPG2 the post-compilation steps will have created path/JSPG2I.JSON and path/JSPG2O.JSON.  Find path from your JCL, and copy these to objects to your equivalent of C:Tutorials\TSTSQL.  Hint: right-click [Process]: you’ll see that the bottom section of this form provides Windows ó z/OS upload and download.

·         Micro Focus:  [Process] creates COBOL, and then passes control to the Micro Focus COBOL project.  Compile the COBOL, then create the web service interface and binding objects as described here, particularly the section headed Prepare Web Services for Testing.  If you are re-creating a client interface after modifying a program, make sure that you’re using the most recent version of the binding objects by deleting and recreating the service interface (.svi) and associated objects (.wsbind, and .json).  See When do you Need to Re-create a Client Interface? for more information.

You will probably have tested that your service works to at least a rudimentary level with a Postman or ReadyAPI test. 

Click [Client] and the Web Service Client Interface dialog appears: -

WebAPI is set with Configure/Client (in Step 3.  Configure Jazz), and is the location of your C# project.  Service and program name come from the PROGRAM statement.  The Service name must be the same as the last level of WebApi. 

If you have already created this interface check [  ] Replace to re-create it.   Local JSON will always be checked: the checkbox is provided to indicate future development, which may provide a browse dialog allowing you to search for the relevant JSON.

Click [Create Interface].  Jazz will create a C# interface object called xxxxClient.cs, e.g. JSPG2Client.cs, and if you have checked [  ] Web or [  ] Windows it will create further objects as “templates” for you to adapt that will use the interface.  Here we’ve checked [ü] Windows, so a Windows form named TestJSPG2.cs will be created into your C# project.   Further options (VB.Net, Java, etc) will be developed.   Planned, not yet available.

If errors are detected then messages are reported as Jazz Error messages, following the Program statement: -

Step 1.  Locating JSON and Creating the Message Descriptions.

When [Create Interface] is clicked, Jazz first locates the JSON descriptions of the web service.  After you’ve turned your Jazz program into COBOL, compiled this, and created the support objects (“Binding file” or “WSBIND”), three .json files should have been written into your project file.  For example, my project file (which I set with [Configure]) is C:\tutorials\TstSQL.   For program JSPG2 C:\tutorials\TstSQL\ contains these three .json files: -

·         JSPG2.json           This is created directly by Jazz when you generated program JSPG2: it is a Swagger definition that defines the service and has references to the message formats (schemas) JSPG2I and JSPG2O, which don’t exist when it is created.

·         JSPG2I.json          This describes the Input (Request) message.  It is created later, by either ZOS or Micro Focus.  If created by ZOS it will have originally been created on a mainframe, and should have been copied back to your project file.

·         JSPG2O.json        This describes the Output (Response) message.  Like JSPG2 it is created later.

From the Swagger definition Jazz extracts path information (request type – post, put, etc, and path, e.g. http://localhost:9003/cics/services/JSPG2), and references to the schemas for the input and output messages.  The input and output schemas are vital as they describe the format of the messages sent to and from the web service.

If these files can’t be located then message #559 will be produced.  To correct the problem, first check that all the steps after producing COBOL – compile the COBOL, produce binding file and JSON –  have been completed (e.g. by testing the service with a utility like Postman or ReadyAPI).   If you can run such a test then JSON has been produced, although the JSON may still be only on the mainframe.   Locate this JSON manually, and copy it to your project folder.

If the JSON exists then the message definitions are created: -

RequestJSPG2.cs      Created from the input message, JSPG2I, this is the actual structure of the request message. 

ResponseJSPG2.cs   Created from the output message, JSPG2O, this is the actual structure of the response message.

If you were to examine your C# interface project, it would now look like this except for the client object (JSPG2Client.cs) which will be created in the next step.

Step 1 has created the Request and Response objects, like the examples below.  Next Jazz creates the interface object, JSPG2Client. 

Step2.  Creating the Client Interface

An object like JSPG2Client provides one or more methods to communicate with the web service.   Program JSPG2 requires at least four methods, Enquiry, Update, Add, and Delete, corresponding to the possible values of Function, and there are also ReadNext, ReadLast, ReadPrev and ReadFirst methods because the Enquiry offers a scrolling option if you read records by the duplicate key, WorkDept.  Other clients might include Logon, Logoff, New Order, Process Order etc.

The process starts by reading a template, @JazzClient.cs, into memory, substituting values for @Service (=MyJSv) and @Program(=JSPG2), and removing markers like @Private after pointers are set to the preceding lines.  The Client object is then generated based on all the information that is available to program JSPG2.  You can learn more about it at Understanding the Client Interface.

Server-side and Client-Side Validation

The purpose of a client interface like JSPG2Client.cs is to make it much easier for client programs to interface with the web service, and to provide local validation so that errors are detected instantly rather than with the delay inherent in a Request/Response cycle.  However local and remote validation rules may differ:

1.    Validation requiring further I/O, such as an EXISTS property, is not checked locally.  For example, Employee.Workdept is defined

    WORKDEPT CHAR(3) CAPS DKEY 'XEMP2' EXISTS DEPARTMENT.DEPTNO,

To be valid any value entered for WorkDept must exist as a value of DEPARTMENT.DEPTNO, the primary key of the DEPARTMENT table.   This is not checked within JSPG2Client.  There would be no point in doing this: it can only be checked by sending a request to a web service, incurring the response delay that local validation is supposed to prevent.

2.    CHECKR properties are defined in the record definition to specify Check Routines, which are routines (paragraphs or subprograms) that check particular fields, for example a bank account, social security number, etc.  The web service knows their names and interface rules, but not their internal logic, and they may be written in COBOL or any language that can be called from a COBOL program. A Check Routine might even require server-side I/O, for example looking up reference tables.  Like EXISTS, these are not checked locally.

Future development: it should be possible to write your own local routines, for example to check that a social security number has the correct format, and use these in an interface. 

3.    Some local validation may follow different rules because formats are different, or COBOL and C# follow different rules.  Particular examples:

a.           A Jazz BOOLEAN field is usually CHAR(1) with ‘Y’ meaning ‘true’ and not ‘Y’ meaning ‘false’.  The interface will render these as ‘true’ and ‘false’, and expect input as “t’ or ‘f’ (which will be displayed by the client interface as “true” or “false”, but be sent to the service as “Y” or “N”).

b.           DATE fields are transmitted in a standard form (Integer, format yyyymmdd), whatever the culture settings at either end, but are displayed and managed with your local culture settings.  For example, BirthDate is transmitted as 19560527, but with New Zealand culture settings will be displayed and edited as 27/05/1956.  With U.S. settings it would be displayed and edited as 5/27/1956.

For these reasons, and also because the service cannot guarantee that the message it receives has come from the web service client interface, request messages are fully validated by the service before they are accepted for processing.  With good client-side validation most of the possible errors will have been prevented, but the cost of repeated validation is a small price to pay for the assurance it provides.  Should an error be detected in the service it will be reported in the returned Error value.

When do you Need to Re-create a Client Interface?

If you have modified your program in a way that doesn’t change the message formats or operations supported by the service, then you can generate and compile the COBOL program but you do not need to re-create the client interface.  For example, minor changes to logic like a different message, or a changed calculation, will have no effect on the message formats, and you can test your new program with its client without re-creating the interface.  However, if you change the message format: for example you change the list of fields to be sent in either the input or output message, or if you add or remove a method (Update, Add, Delete, etc) compared to the previous message, then you’ll need to regenerate the interface.  

You’ll also need to regenerate the interface if you change between returning 1 record or several.  I generated program JSPG2 to return 1 EMPLOYEE record, and program JSPG2A to return 6.   The difference between 1 and 6 means that for JSPG2A the EMPLOYEE data is returned in an array, and so the interfaces are different.  Because EMPLOYEE data is returned as an array for JSPG2A, but not for JSPG2 (even though the COBOL record structure is defined with OCCURS 1), JSPG2 and JSPG2A require different interfaces.  The interface need not be re-created if JSPG2A is changed to return a different number, provided that it is > 1.

Note that, “Array” means something different in the world of COBOL, and object-oriented languages like Java and C#.  To a COBOL programmer an array is a repeating set of identical structures mapped to a region of memory, and COBOL programmers can take advantage of this by redefining the array.  In the client-side world of C# amd Java, arrays are lists.  They are not necessarily in a contiguous region of memory, so Array(2) might or might not follow Array(1). Fields of the array may be directly within the array element, more often they are pointers to data stored elsewhere.  Elements don’t have to be the same size.  They may even differ in format.  They cannot be redefined: there is no equivalent of a COBOL REDEFINES clause.

To Re-create a Client Interface

With Micro Focus Enterprise Developer: -

In Solution Explorer, right-click your COBOL project and click Refresh Annotations.  

Select your program or click [Auto Select], and then click [Refresh]

Delete the old .svi, .wsbind, and .json objects, e.g. JSPG2A.svi, JSPG2A.wsbind*, JSPG2AI.json, and JSPG2AO.json
*           The .wsbind may have been moved

Now recreate the .svi, and wsbind etc. as described here.

With z/OS: -

[Process] your program to submit a job that will compile your program and [re-]create the binding objects including the .json

Copy the two .json files back to your project folder, e.g. your equivalent of C:\Tutorials\TSTSQL

Using a Client Interface

The section above, Creating a Client Interface, dealt with creating the middle section of this diagram.   Creating this is simply a matter of clicking [Client] and then [Create Interface] from the Jazz Workbench with the program that created the web service (right section, “Mainframe”).  If changes are made to the service, you can re-generate the interface.

You’ll develop a client – a Windows Form, a Web page, or a mobile App – that communicates with the web service through the interface to use the interface.  This client might be developed in C#, Java, VB.NET, or other languages. This is the left part of this diagram.

 

Clients will communicate with the interface through properties and methods, which can be discovered in the usual way.  This section describes how you can write such a client: TestJSPG2 is a Windows form that uses JSPG2Client to communicate with the web service.  This section shows how you would write such a client, using Visual Studio.

It is planned that MANASYS will generate prototype clients, like the Windows form used in this example.  These are unlikely to be anything more than a code template, from which you can extract useful code snippets.   A real client may invoke other web services, access local data, or services like the client’s current GPS location.

Getting Started – Locate or Create a Suitable Project

Client objects are created within a suitable project – for example, if you want to create a C# Windows client, then you must have a C# Windows project.  For a VB Windows Client you’ll need a VB Windows project, and so on.  If such a project doesn’t exist, you can create a new project.

To create a suitable C# Windows Project: -

1.    Open Visual Studio, select New Project, and choose a suitable template.  I chose Windows Forms App (.NET Core).  Click [Next]

2.    On the next form

a.    give the project a name (I used “MyJSvClientTests”),

b.    optionally check [  ] Place solution and Project in the same directory (recommended), and

c.     click [Create].   A project is created with these objects: -

3.    Right-click Solution ‘MyJSvClientTests’, and select Add/Existing Project.  A Windows Explorer opens showing you your projects.  Click on folder MyJSv, and then when that folder opens, click on MyJSv.csproj and then click [Open] : -

 

The project now looks like this: -

4.    Rename Form1 to something with more meaning.  I renamed it to JSPG2Test: -

a.           Right-click Form1.cs, click Rename

b.           Change “Form1” to “JSPG2Test”. 

c.           Reply [Yes] to the message “Would you also like to perform a rename in this project to all references to the code element ‘Form1’?

d.           Open the form JSPG2Test and change its Text property from Form1 to JSPG2Test to be consistent with its object name

 

5.    Right-click the Project (NOT the Solution) MyJSvClientTests and select Project Reference.   Because we have already included this project within our Solution, it is the first option: -

Click this checkbox.

Creating More Forms

I have followed this naming convention as I developed more tests: my project MyJSvClientTests currently contains JSPG1Test.cs, JSPG1VTest.cs, JSPG2ATest.cs, JZPG2Test.cs, and JSTIMETest.cs.  Each program uses the corresponding xxxxClient.cs in project MyJSv. 

 

To add another form, in Solution Explorer right-click your test project (MyJSvClientTests), and click Add, and then Form(Windows Form): -

 

Give the form the name that you want (e.g.) JSPG3DTest.  An empty form appears, it already has the correct name so you don’t need to change this.

Initializing the form

Before we can add buttons to invoke interface methods, and labels and textboxes to display data, we need to add some code to the form.  We have already added a reference to project MyJSv in step #5.   Now, with each new test form: -

 

6.  We need to instantiate an interface object within our form. Edit the code for form JSPG2Test (if it is not already open in Visual Studio, right-click JSPG2Test.cs in Solution Explorer, and click View Code).  Add

    using MyJSv;
towards the top. 

7.  Add
   static JSPG2Client Jspg2Client = new JSPG2Client();
where shown in the code below.  Note that we’ve used the same name, but with different case:  Jspg2Client is an instance of the class JSPG2Client.

8.  We also add

bool EditMode = true;

      The reason for this will become clear shortly.

The C# code for our form now looks like this: -

using MyJSv;

using System.Windows.Forms;

 

namespace MyJSvClientTests

{

    public partial class JSPG2Test : Form

    {

        static JSPG2Client Jspg2Client = new JSPG2Client();

        bool EditMode = true;

        public JSPG2Test()

        {

            InitializeComponent();

        }

    }

}

The following notes detail how you create your first form. Go to Creating Similar Tests to find some shortcuts to reuse your earlier tests.

Creating a Client Form

Now that we’ve created a suitable form object, JSPG2Test, and instantiated the interface object JSPG2Client as Jspg2Client  (same name, different capitalization), we’re ready to design the form (or web page or app or …) by adding controls (labels, textboxes, buttons) to communicate with Jspg2Client properties and methods.  As you develop the form, you’ll develop the form’s logic, described in the next section.   In reality you’ll flip-flop between these two sections, but it’s easier to describe appearance and Form Logic separately.

Here is a form that I created to test and demonstrate the interface: -

I’ve created

·         label fields lblError, lblBrowseCount and lblReturnCode to display various output fields returned by the service,

·         textboxes for fields that can be entered by users and passed to the web service and also returned from the service.  I arranged the fields used for access (EMPNO, WORKDEPT, and SKIP) at the top, and highlighted the two key fields.

·         Access by WORKDEPT might return several values, for example WORKDEPT=D11 corresponds to ~ 14 records in my test data.  The form needs scrolling commands as shown to manage this, arranged in a Groupbox so that the control group can be displayed or not depending on whether scrolling is relevant.

·         lblSex to display the value of the Sex code,

·         lblHttpResponse to display response header information, and lblRawResponse to display the response if the request fails.

·         a series of buttons for the methods that the interface supports, plus [Clear] and [Close].  

This form uses almost all of the data and methods available, but in a real situation you might use only some of the interface’s fields and methods. As well as the buttons like btnEnquire that invoke interface methods, [Clear] empties the form controls, and [Close] closes it.

How do you know what data (properties) and methods (operations) are available?   You can find out from Intellisense, or from the source code.

Intellisense

Within the logic of a control write the name of the service interface (Jspg2Client) followed by a period.  This name comes from
           
static JSPG2Client Jspg2Client = new JSPG2Client();

that we wrote above.  Thus we double-click a control, and write “Jspg2client.” in its event logic:-
        private void txtEmpno_TextChanged(object sender, EventArgs e)

        {

            {

                Jspg2Client.

            }

        }

As we type the “.” Intellisense responds by showing you a list of properties and methods in the interface: -

Source Code

The source code of the service interface provides useful data.

1.    A few lines from the beginning you’ll find a definition of struct SetByUser. This provides a list of all the fields that can be changed by the client: these should become textboxes.   I have named my textboxes txtXxxx, e.g. txtEmpno, and placed a label ahead of them with their name (EMPNO).

2.    A little further down there is a comment
     
// Key Properties - fields used by methods to access records
The following properties are particularly important: they must all be present in your client program, as they are used to tell the methods which records you want to work with.  For JSPG2Client there is a field called Skip used to control scrolling, and two fields EMPNO and WORKDEPT.  EMPNO is the primary key of EMPLOYEE, and WORKDEPT is a duplicate key.  Skip is used when you access the table by WORKDEPT, providing an ability to handle multiple records with scrolling.

3.    Next is a comment
     
//  I/O Data - non-key fields that may be set for update and add, will be set in response
These properties are general non-key fields that the client might use, and might change.  Like the key properties, they should become textboxes if you want them.

4.    Coded fields and _Value properties.    Some fields in the web service have been defined as codes: for example SEX is defined: -

SEX CHAR(1) CAPS CODES(M:Male, F:Female),

The value which you can enter in the text box is the code – “M” or “F”.   Because SEX is defined with codes, a readonly property SEX_Value is created which will contain “Male” or “Female”.

Next there are output fields
     
//  Output data - readonly properties that are set from response, e.g. JZ_Error
You should put these into labels, not textboxes, because the client can’t change their value.  Of these, Error is particularly important.  If any errors are detected then text will be returned in this field, and from a normal response a message like SQL UPDATE/INSERT SUCCESSFUL will be displayed.  The fields JZ_Employee_BrowseCount and JZ_Employee_Return code are useful to control scrolling. Return Code is another example of a coded field: it is defined
    ReturnCode CHAR(1) CAPS CODES(' ':'    ',W:Warning,E:Error,S:'Serious Error',T:'Terminal Error',
                       A:Absent,C:Changed,D:Deleted,F:First,I:Inserted, L:Last, N:Nth,U:Unique));

and there is a ReturnCode_Value property in case you want to display “Absent”, etc.

5.    Following the output fields are the methods
     
//  Methods.  Usually related to Function, e.g. Function CHAR(1) CAPS CODES(E:Enquiry, U:Update, A:Add, D:Delete) will have
   //      Enquiry, Update, Add, and Delete methods

This section contains code for each method, like
     
public bool Enquiry(string PSkip = null,string PEMPNO = null,string PWORKDEPT = null)
and the code to implement this method.  You should not need to be concerned with this implementation, but you will need to put a Button in your client for each method that you want to use, and write appropriate code to invoke the method.  We’ll see how to do this later. Form Logic - Properties

Double-click a TextBox and a TextChanged handler is added.  Using Intellisence we can select a Key or I/O property of the interface, and complete the statement by assigning the textbox’s text value to this property
        private void txtEmpno_TextChanged(object sender, System.EventArgs e)

        {

            Jspg2Client.EMPNO = txtEmpno.Text;

      }

As values are typed into the textbox they are validated according to the rules that have been built into the set option for the Jspg2Client property.  You will remember that EMPNO is numeric, and has range 0 to 999999.   txtEmpno_TextChanged will be invoked whenever we key a value into txtEmpno.Text, so if we attempt to enter a non-numeric character we’ll immediately get the error message and return code displayed.  However txtEmpno_TextChanged is also triggered when a value is returned from an Enquiry (or other) method.  We don’t want this, so an if condition is added so that the code is only executed when EditMode is true: -

        private void txtEmpno_TextChanged(object sender, EventArgs e)

        {  

            if (EditMode)

            {

                Jspg2Client.EMPNO = txtEmpno.Text;

            }

        }

Provided that the value entered obeys this rule the assignment will accept the value, but if the value is invalid it will be rejected and the interface sets output fields Error and a return code, so we write code like this. The two lines setting lblError.text and lblReturnCode.text will occur many times in the client, so they’re put into a routine: -

        private void txtEmpno_TextChanged(object sender, EventArgs e)

        {  

            if (EditMode)

            {

                Jspg2Client.EMPNO = txtEmpno.Text;

                ShowResponse();

            }

        }

        private void ShowResponse()

        {

            lblError.Text = Jspg2Client.Error;

            lblReturnCode.Text = Jspg2Client.JZ_Employee_ReturnCode + ":" + Jspg2Client.JZ_Employee_ReturnCode_Value;

        }

We should write similar code for all of the textboxes.

Form Logic – Methods

Double-click a button like [Enquiry] and a Click handler is added:

        private void btnEnquiry_Click(object sender, EventArgs e)

        {

        }

We write code here to invoke the interface’s Enquiry method, and handle its response.  We start by writing a reference to the interface, and selecting the Enquiry method.  Intellisense shows us the parameters that the method requires.

Note also that interface methods are functions returning True or False, as they’re designed to be written as conditions of an IF statement. We add this logic to invoke Jspg2Client.Enquiry: -
        private void btnEnquiry_Click(object sender, EventArgs e)

        {

            if (Jspg2Client.Enquiry(txtSkip.Text, txtEmpno.Text, txtWorkdept.Text))

            {

                HandleValidResponse();

            }

            else

            {

                ShowResponse();

            }

        }

If there is a problem then the method returns False and only the Error and Return Code properties.  However, if the interface request succeeds a response is received with data in all the interface’s properties, so we execute HandleValidResponse to copy the data from these to our client’s controls like txtEMPNO.  All of the methods will have the same action on return, so we change this to

        private void btnEnquiry_Click(object sender, EventArgs e)

        {

            HandleResponse(Jspg2Client.Enquiry(txtSkip.Text, txtEmpno.Text, txtWorkdept.Text));

        }

        private void HandleResponse(bool Method)

        {

            if (Method)

            {

                HandleValidResponse();

            }

            else

            {

                ShowResponse();

            }

        }

If there is no need to handle scrolling, HandleValidResponse is just this: -

        private void HandleValidResponse()

        {

            SetFormValues();

        }

SetFormValues simply assigns all the interface properties to corresponding form controls. 
        private void SetFormValues()

        {

            EditMode = false;

            lblError.Text = Jspg2Client.Error;

            lblBrowseCount.Text = Jspg2Client.JZ_Employee_BrowseCount;

            lblReturnCode.Text = Jspg2Client.JZ_Employee_ReturnCode + ":" + Jspg2Client.JZ_Employee_ReturnCode_Value;

            txtEmpno.Text = Jspg2Client.EMPNO;

            txtWorkdept.Text = Jspg2Client.WORKDEPT;

            txtSkip.Text = Jspg2Client.Skip;

            txtFirstNme.Text = Jspg2Client.FIRSTNME;

            txtMidInit.Text = Jspg2Client.MIDINIT;

            txtLastName.Text = Jspg2Client.LASTNAME;

            txtPhoneNo.Text = Jspg2Client.PHONENO;

            txtHireDate.Text = Jspg2Client.HIREDATE;

            txtJob.Text = Jspg2Client.JOB;

            txtEdlevel.Text = Jspg2Client.EDLEVEL;

            txtSex.Text = Jspg2Client.SEX;

            lblSex.Text = Jspg2Client.SEX_Value;

            txtBirthDate.Text = Jspg2Client.BIRTHDATE;

            txtSalary.Text = Jspg2Client.SALARY;

            txtBonus.Text = Jspg2Client.BONUS;

            txtComm.Text = Jspg2Client.COMM;

            txtCurrency.Text = Jspg2Client.CURRENCY;

            txtDeptMgr.Text = Jspg2Client.DEPTMGR;

            EditMode = true;

        }

It is written as a subroutine as it will be used for other methods.   Note that it starts by setting EditMode = false; and concludes with EditMode = true; so that the assignments don’t execute the code in the TextChanged handlers.  This would not only be pointless but could have some undesirable side effects such as recording the field as Changed (= set by user) meaning that it will be included in a following Update.

Managing Scrolling

Program JSPG2 retrieves Employee records using either Employee’s primary key, EMPNO, or WORKDEPT.  EMPNO may retrieve zero or 1 record, WORKDEPT may retrieve zero or many records.   To handle scrolling I’ve placed buttons and the Skip textbox on the form within a groupbox: -

When the enquiry uses the duplicate key, WORKDEPT, these scrolling controls will appear if there is more than one Employee for this WORKDEPT value, and the scrolling buttons will be active if there are more records in the Next or Previous direction.  Two output fields are important for controlling scrolling: -

·         JZ-Employee-BrowseCount is set to 0 for access by unique key (EMPNO), and to the number of records that could be retrieved for non-unique access (WORKDEPT).  We do not want the scrolling controls if BrowseCount is 0 or 1.

·         If scrolling applies, then the return code, defined as

    JZ-Employee-ReturnCode CHAR(1) CAPS CODES(' ':'    ',W:Warning,E:Error,S:'Serious Error',T:'Terminal Error',
                       A:Absent,C:Changed,D:Deleted,F:First,I:Inserted, L:Last, N:Nth,U:Unique));

can tell us whether where at the beginning or end of the scrolling list.  For ReturnCode F (First) we don’t want the Previous [ < ] and First [ << ] buttons active, for Return code L (Last) we don’t want Next [ > ] or Last [ >> ].

Note 1.   Field names in Jazz or COBOL that have hyphens will have underscores in C# and other client languages.  Thus these fields are called JZ_Employee_BrowseCount and JZ_Employee_ReturnCode in C#.

Note 2.  BrowseCount is calculated differently for SQL (=DB/2 etc.) and VSAM.   See Browsecount.

Note 3.  It is a bad idea to use a scroll bar with a web service.  Dragging a scroll box causes a flurry of request/responses to be sent to/from the web service).

We’ll need code to be executed with Enquiry to control whether we see the scrolling commands, and whether the Next or Previous buttons are active.   But the scrolling buttons, also need this code, so this logic is put into HandleValidResponse: -

        private void HandleValidResponse()

        {

            int BrowseCount;

            int.TryParse(Jspg2Client.JZ_Employee_BrowseCount, out BrowseCount);

            if (BrowseCount < 2)

            {

                gbxScroll.Visible = false;

            }

            else

            {

                gbxScroll.Visible = true;

                if (Jspg2Client.JZ_Employee_ReturnCode == "F")

                {

                    btnPrev.Enabled = false;

                    btnFirst.Enabled = false;

                }

                else

                {

                    btnPrev.Enabled = true;

                    btnFirst.Enabled = true;

                }

                if (Jspg2Client.JZ_Employee_ReturnCode == "L")

                {

                    btnNext.Enabled = false;

                    btnLast.Enabled = false;

                }

                else

                {

                    btnNext.Enabled = true;

                    btnLast.Enabled = true;

                }

            }

            SetFormValues();

        }

 

Because this web service may require scrolling, the interface has been generated with ReadNext, ReadLast, ReadFirst and ReadPrev methods.  IntelliSense discovers these, and we add methods like this: -

        private void btnNext_Click(object sender, EventArgs e)

        {

            HandleResponse(Jspg2Client.ReadNext(txtSkip.Text));          

        }

        private void btnPrev_Click(object sender, EventArgs e)

        {

            HandleResponse(Jspg2Client.ReadPrev(txtSkip.Text));

        }

        private void btnLast_Click(object sender, EventArgs e)

        {

            HandleResponse(Jspg2Client.ReadLast());

        }

        private void btnFirst_Click(object sender, EventArgs e)

        {

            HandleResponse(Jspg2Client.ReadFirst());

        }

Update, Delete, and Add

In the same way we discover that Update, Add, and Delete require a single argument, EMPNO, which is Employee’s primary key, so except for the method name we write mostly the same code for each.  Here is the Update logic: -

        private void btnUpdate_Click(object sender, EventArgs e)

        {

            HandleResponse(Jspg2Client.Update(txtEmpno.Text));

        }

Update must follow an enquiry, either a single-record enquiry using the primary key (EMPNO) and the [Enquiry] button, or a scrolling enquiry with ReadNext or ReadPrev.  The enquiry will have returned a checksum which is used to prevent invalid updates.  If, between your enquiry and update another user changes the record then a different checksum will be calculated: this difference will be detected within the interface, and your update rejected.  NB: since this snap was recorded the check message has changed: it would now be
            GET check failure: EMPLOYEE changed or not read

To update a record we’ll enter new data in various textboxes, then click the [Update] button.  Here I’ve removed MIDINIT by entering “null” (must be lower case), set JOB to “Manager”, and DEPTMGR to T (for True). 

Click [Update] and the record is updated, and new values displayed: -

Delete is identical except that it invokes Jspg2Client.Delete.   Like Update, it must follow an enquiry: -

        private void btnDelete_Click(object sender, EventArgs e)

        {

            HandleResponse(Jspg2Client.Delete(txtEmpno.Text));

        }

Add requires some extra logic.  If it were the same as Update then it would be misleading: Update only sends the changed data to the service except for the record key (EMPNO).  With the Add method you want all the data that you see displayed in the form’s controls to be included in the new record, whether you’ve edited it or whether it has come from a preceding read.  Before the Add the btnAdd_Click event therefore sets the Jspg2Client property corresponding to each of the textboxes except the primary key (EMPNO) and Skip.   Unlike HandleValidResponse/SetFormValues these assignments are NOT surrounded by EditMode = false; and EditMode = true; because the point of these assignments is to set the relevant Changed flag.

        private void btnAdd_Click(object sender, EventArgs e)

        {

            // Input all textboxes (except 1ry Key and Skip), and set their Changed flag

            Jspg2Client.WORKDEPT = txtWorkdept.Text;

            Jspg2Client.FIRSTNME = txtFirstNme.Text;

            Jspg2Client.MIDINIT = txtMidInit.Text;

            Jspg2Client.LASTNAME = txtLastName.Text;

            Jspg2Client.PHONENO = txtPhoneNo.Text;

            Jspg2Client.HIREDATE = txtHireDate.Text;

            Jspg2Client.JOB = txtJob.Text;

            Jspg2Client.EDLEVEL = txtEdlevel.Text;

            Jspg2Client.SEX = txtSex.Text;

            Jspg2Client.BIRTHDATE = txtBirthDate.Text;

            Jspg2Client.SALARY = txtSalary.Text;

            Jspg2Client.BONUS = txtBonus.Text;

            Jspg2Client.COMM = txtComm.Text;

            Jspg2Client.CURRENCY = txtCurrency.Text;

 

            // Now add the new record

            HandleResponse(Jspg2Client.Add(""));

        }

Clear and Close

These are basic methods that do not involve the web service. 

 

Clear simply sets all the textboxes to blank.  This must be done with EditMode = false; to prevent error messages from blank required fields

        private void btnClear_Click(object sender, EventArgs e)

        {

            EditMode = false;

            lblError.Text = "";

            lblBrowseCount.Text = "";

            lblReturnCode.Text = "";

            txtEmpno.Text = "";

            txtWorkdept.Text = "";

            txtSkip.Text = "0";

            txtFirstNme.Text = "";

            txtMidInit.Text = "";

            txtLastName.Text = "";

            txtPhoneNo.Text = "";

            txtHireDate.Text = "";

            txtJob.Text = "";

            txtEdlevel.Text = "";

            txtSex.Text = "";

            txtBirthDate.Text = "";

            txtSalary.Text = "";

            txtBonus.Text = "";

            txtComm.Text = "";

            txtCurrency.Text = "";

            EditMode = true;

        }

Close is

        private void btnClose_Click(object sender, EventArgs e)

        {

            this.Close();

        }

Creating Similar Tests

Having developed one test, you can save time on your later tests.  Here I am developing a test for web service JSPG3D, which is a single-table enquiry reading JSPG3D to read the Department table.  At this point I have created an empty form called JSPG3DTest, and I’ve added the code described in initializing the form. Now I want to add controls to this form, which are similar to the controls on a previous test, JSPG1Test which read the Employee table. 

1.            I opened form JSPG1Test.cs (design view), and selected the part of the form containing the controls I wanted. 

2.            I copy/pasted these controls into form JSPG3DTest

3.            I then edited the form to

a.    Change text to the appropriate field names, e.g. “EMPNO” becomes “DEPTNO”

b.    Change the names of controls that I’d be referring to appropriate names, e.g. “txtEmpno” becomes “txtDeptno”

c.     Add extra controls as necessary: where Employee had one alternate index, Department has two

Now the form looks like this, with the controls that I wanted, but no logic beyond that from initializing the form: -

Now the controls are wired up as described above.  Click on the relevant control, and add similar code.  Sometimes you can simply Copy/Paste code from the copied form (e.g. [Close]), sometimes it is worthwhile to copy/paste and then edit the code, but often it is just as easy to create new code following the patterns above.

Client Projects with Multiple Forms

In Using a Client Interface project MyJSvTests was created with a single form, JSPG2Test. Starting this project with Visual Studio automatically opens form JSPG2Test.   Now you want to test another interface?   You could of course create another project, but if your new client is another C# form then you may prefer to add this form to MyJScTests.

When you create a C# forms project, one of the objects created in it is Program.cs, containing this code: -

using System;

using System.Collections.Generic;

using System.Linq;

using System.Threading.Tasks;

using System.Windows.Forms;

 

namespace MyJSvClientTests

{

    static class Program

    {

        /// <summary>

        ///  The main entry point for the application.

        /// </summary>

        [STAThread]

        static void Main()

        {

            Application.SetHighDpiMode(HighDpiMode.SystemAware);

            Application.EnableVisualStyles();

            Application.SetCompatibleTextRenderingDefault(false);

            Application.Run(new JSPG2Test());

        }

    }

}

My next task was to test JSPG2A, a version of JSPG2 that returns several Employee records.  If I add
           
Application.Run(new JSPG2ATest());
to this, then the project will first open JSPG2test, then when I close this it will open JSPG2ATest.   This worked well when I was testing for differences in behaviour between JSPG2 and JSPG2A.   When I was working with just one of the programs I’d comment out the Application.Run that I didn’t want.  I didn’t bother creating a menu. Currently the logic in MyJSvClientTests.Program is like this: -

        static void Main()

        {

            Application.SetHighDpiMode(HighDpiMode.SystemAware);

            Application.EnableVisualStyles();

            Application.SetCompatibleTextRenderingDefault(false);

            //Application.Run(new JSPG1Test());

            //Application.Run(new JSPG1VTest());

            //Application.Run(new JSPG2Test());

            //Application.Run(new JSPG2ATest());

            Application.Run(new JSTimeTest());

        }

For complete details of the JSPG2A test, see Programs used as Examples

Parent-Child Services

Parent/Child Services, as described here, require Build 16.3 or later.

Introduction: Test Data for JSPG3

So far we have dealt with services that handle one type of record, either for Enquiry only, or with Update, Add, and Delete functions.  We can also create services that handle a parent record and a set of child records.  We’ll illustrate this with program JSPG3 which, like JSPG2, is a web service capable of updating DB2 data.  It works with two records from the IBM Sample database, Department, and Employee, defined to MANASYS like this: -

*# Created from Table:DEPARTMENT, Schema:ROBERTBW10, Database:Sample by JazzUser at 23/03/2019 7:52:20 AM

DEFINE DEPARTMENT SQL DATA(

    DEPTNO CHAR(3) CAPS REQUIRED KEY,

    DEPTNAME VARCHAR(36) REQUIRED,

    MGRNO CHAR(6) PIC '999999' DKEY 'XDEPT2',

    ADMRDEPT CHAR(3) CAPS REQUIRED DKEY 'XDEPT3',

    LOCATION CHAR(16));

*# Created from Table:EMPLOYEE, Schema:ROBERTBW10, Database:Sample by JazzUser at 26/03/2019 4:25:46 PM

COPY Department;

DEFINE EMPLOYEE SQL DATA(   

    EMPNO CHAR(6) PIC '999999' REQUIRED KEY,

    FIRSTNME VARCHAR(12) REQUIRED,

    MIDINIT CHAR (1),

    LASTNAME VARCHAR(15) REQUIRED,

    WORKDEPT CHAR(3) CAPS DKEY 'XEMP2' EXISTS DEPARTMENT.DEPTNO,

    PHONENO CHAR(4) PIC '9999',

    HIREDATE DATE,

    JOB CHAR(8),

    EDLEVEL SMALLINT REQUIRED,

    SEX CHAR(1) CAPS CODES(M:Male, F:Female),

    BIRTHDATE DATE,

    SALARY MONEY(9,2) MIN 0,

    BONUS MONEY(9,2) MIN 0,

    COMM MONEY(9,2) MIN 0,

    CURRENCY CHAR(3),

    DEPTMGR BOOLEAN);

As the initial comments indicate, these MANASYS record definitions were initially created using Import from SQL, but then the definitions were enhanced with Jazz properties like PIC '999999', CAPS, CODES(M:Male, F:Female), and others to provide better editing and make programming easier. 

Department has primary key DEPTNO, and two duplicate keys, MGRNO and ADMRDEPT.  We can access the table by any of these keys, for example: -

db2 => Select * from Department where admrdept = 'A00'

DEPTNO DEPTNAME                             MGRNO  ADMRDEPT LOCATION

------ ------------------------------------ ------ -------- ----------------

A00    SPIFFY COMPUTER SERVICE DIVISION     000010 A00      Head Office

B01    Planning.                            000020 A00      Building G

C01    INFORMATION CENTER                   000030 A00      -

D01    DEVELOPMENT CENTER                   -      A00      -

E01    SUPPORT SERVICES                     000050 A00      -

  5 record(s) selected.

Employee records are related to Department through WorkDept: -

WORKDEPT CHAR(3) CAPS DKEY 'XEMP2' EXISTS DEPARTMENT.DEPTNO,

Of the 5 departments in the list above, some have employees: -

db2 => Select count(*), workdept from Employee where workdept in (Select DEPTNO from department where admrdept = 'A00') Group by workdept

1           WORKDEPT

----------- --------

         30 A00

          1 B01

          4 C01

          1 E01

  4 record(s) selected.

Creating Web Service JSPG3

Here we’re creating a service that will return a Department record, and 0 to 10 employees (at a time) who work in that department: -

[Finish] creates a Jazz web service program, JSPG3, that that can read and update Department much as program JSPG2 could read and update Employee.  But unlike JSPG2, with each Department the related Employee records are also read: the [Finish] process deduces the relationship between the two tables from the EXISTS DEPARTMENT.DEPTNO property of EMPLOYEE.WORKDEPT.  The web service message definition, and then the program JSPG3, is displayed in the Jazz Workbench. 

Compared to a single-record service like JSPG2

1.            The Enquiry and Update logic includes code to read the related Employee records: -

    WHEN (Enquiry);

        GET DEPARTMENT KEY(DEPARTMENT.DEPTNO OR DEPARTMENT.MGRNO OR DEPARTMENT.ADMRDEPT) SAVESUM OJSPG3.JZ-DEPARTMENT.Checksum;

            PROCESS EMPLOYEE WHERE(EMPLOYEE.WORKDEPT = DEPARTMENT.DEPTNO) SAVESUM OJSPG3.JZ-EMPLOYEE.Checksum;

            END PROCESS EMPLOYEE RESPOND OJSPG3;

        END GET DEPARTMENT RESPOND OJSPG3;

2.            New functions Child and PChild are included.

    WHEN (Child);

*   Create a new EMPLOYEE record

        GET EMPLOYEE FREEKEY CREATE;

            ACCEPT (IJSPG3.JZ-DEPARTMENT.DEPTNO,IJSPG3.JZ-EMPLOYEE.*) EXCEPT(EMPLOYEE.EMPNO) MESSAGE OJSPG3.Error;

            EMPLOYEE.WORKDEPT = DEPARTMENT.DEPTNO;

            GET DEPARTMENT KEY(DEPARTMENT.DEPTNO);

            END GET DEPARTMENT RESPOND OJSPG3;

        END GET EMPLOYEE CREATE RESPOND OJSPG3;

    WHEN (PChild);

*   Process a selected child record.

*   Generated logic is only a basic update: change this for your situation, e.g. "Process Order".

        ACCEPT (EMPLOYEE.EMPNO=IJSPG3.EMPNO) MESSAGE OJSPG3.Error;

        GET EMPLOYEE  KEY(EMPLOYEE.EMPNO) UPDATE CHECKSUM IJSPG3.JZ-EMPLOYEE.Checksum;

            ACCEPT (IJSPG3.JZ-DEPARTMENT.DEPTNO,IJSPG3.JZ-EMPLOYEE.*) EXCEPT(EMPLOYEE.EMPNO) MESSAGE OJSPG3.Error;

            EMPLOYEE.WORKDEPT = DEPARTMENT.DEPTNO;

            GET DEPARTMENT KEY(DEPARTMENT.DEPTNO);

            END GET DEPARTMENT RESPOND OJSPG3;

        END GET EMPLOYEE UPDATE RESPOND OJSPG3;

If either of these functions are used the logic will start by creating or reading an Employee record, and then it will directly read the corresponding Department record.  The initial Enquiry might have read one of several records and you could scroll forward and back through these, but after a Child or PChild function parent scrolling will be lost until the next Enquiry.

We click [Process] to convert this to COBOL. 

·         If MANASYS is configured to work directly with ZOS, a job will be submitted to ZOS that will compile the COBOL, and generate the JSON and Binding files.

·         If MANASYS is configured to work with Micro Focus, the COBOL is written directly into the relevant folder of the COBOL project.  We click the button to the right of [Process] and control is passed to the MF COBOL project where we compile it, and create the JSON and binding object as previously described.

Once the JSON has been created (and for ZOS users, copied back to the Jazz Workbench environment) the service can be tested with general test software such as Postman or ReadyAPI.  Here is a ReadyAPI test of program JSPG3: -

This test shows that the service is working, and when invoked with ADMRDEPT = “A00” it will return the first of 5 qualifying DEPARTMENT record, plus the first 10 of 30 records of employees working in this department.

Creating and testing JSPG3Client

Creating a client interface, JSPG3Client.cs, is as described previously for program JSPG2.  With program JSPG3 displayed in the Jazz workbench, click [Client].

To create a client to invoke this, I’ve created JSPG3Test.cs starting by creating this C# Form, copying and renaming controls from previous forms (see Creating Similar Tests) and hooking up the initialization: -

 

The top part of this form is very like JSPG2Test except that it reads and updates Department instead of Employee.  The bottom part of this form is also like JSPG2Test, showing Employee fields and controls.   The controls are a little bit different, and note that it has its own scrolling controls.  At this stage we have a form, but without any code-behind logic to make the buttons do anything, or the textboxes and labels to display any data.

Next I wired up the textboxes to provide get and set methods, and wired up all the buttons.  In the top (Department) part of the form, the scrolling buttons are wired up to the Scroll methods and Enquiry, Update, Add, Delete, Clear, and Close are wired up as in JSPG2, except for the obvious differences in object names. 

In the bottom (Employee) section the scrolling buttons are wired up to a set of ChildScroll commands (ReadNextChild, ReadLastChild, ReadPrevChild, and ReadFirstChild), [Add Employee] is wired up to Child, [Process Employee] to PChild, and [Clear Employee] to logic that clears the Employee fields except for WorkDept.   WorkDept is a special field: it defines the joining relationship, and the Jazz code
           
PROCESS EMPLOYEE WHERE(EMPLOYEE.WORKDEPT = DEPARTMENT.DEPTNO) …
ensures that any Employee handled by JSPG3 MUST have a WORKDEPT value = DEPNO.   When we created the service the option “Xcpt Join” omitted the joining fields from Employee, so Workdept is not in the response message.   In test JSPG3Test I created a textbox for Workdept, but I wired it up to DEPTNO, and made it read only.

Here is a test.  I have just clicked [Clear] to clear the whole form, then set ADMRDEPT to a00 and clicked [Enquiry]

Scrolling through the 5 Department records is, like JSPG2, relatively slow, as each Next or Previous requires a request/response.  The response returns the first 10 employee records, and JSPG3Test displays the first.   Scrolling through the Employee records behaves like program JSPG2A: there is a slow response when we first ask for the 11th record, or the 21st, but when Next or Previous requires a record that has already been read, the response is instantaneous.   Whenever we scroll the Department Records, the employee list is cleared and reset. 

Clicking various action buttons will Update, Delete, or Add Department records, while clicking [Add Employee] or [Process Employee] invokes the Child or PChild methods to add or process the Employee records.  As noted above, using either of these two buttons will fix the Department record so you need to click [Enquiry] to resume Department scrolling.

For complete details of the JSPG3 test, see Programs used as Examples

Sample Clients

It is planned that when you generate an interface you can check one or more of the Client options: -

This will generate a sample client with code like that described above, in the option(s) that you choose.  Initial validation of this will be with a C# Windows Form, then C# Web Program, then Visual Basic, then Java.  Further sample client options will be provided if required.

These sample clients are intended to provide initial code for users to start with, but we expect that they will need significant editing to meet actual requirements.  As well as dealing with the web service, the client program may access local data, interact with other web services, and be part of a larger conversation involving authentication and data passing from step to step. 

Appendices

Programs used as Examples

MANASYS Jazz programs

Program

Function

Comment

JSPG1

DB2 Enquiry

Generated web service, JSON, Uses Database Sample, Table Employee, max = 6.

JSPG1V

VSAM Enquiry

Like JSPG1, but uses VSAM file Custf

JSPG2

DB2 Update

Generated like JSPG1 except Update, max = 1

JSPG2A

DB2 Update

Like JSPG2 except max = 6

JSTIME

Returns Server’s Time

A very basic program, with no file or database I/O

JSPG3

DB2 Update

Parent/Child Update (Department/Employee)

You can click the links to see the MANASYS test programs used.  They will open in Notepad, copy them to a .jzz file if you have MANASYS Jazz available.

Client Interfaces

JSPG2AClient

JSPG3Client

Files will open with Notepad

Test Programs

JSPG2A

JSPG2ATest.cs

JSPG2ATest.Designer.cs

JSPG2ATest.resx

JSPG3

JSPG3Test.cs

JSPG3Test.Designer.cs

JSPG3Test.resx

Returning Multiple Records

Programs JSPG2 and JSPG2A have been generated to perform the same task, but JSPG2 was defined with Max=1 and JSPG2A with Max=6.  The process of defining the interface JSPG2AClient and the test form JSPG2ATest is exactly as described above for JSPG2, and both tests behaved identically except for scrolling: for JSPG2A, ReadNext ([>] button) was instant (< 1 millisecond) except for every 6th record when a Request/Response was required.  Each Request/Response took approximately 3 seconds with my test environment, and I couldn’t find any statistically-significant difference between request/responses from JSPG2 and JSPG2A even though JSPG2A’s messages were larger.  While returning large output messages than necessary seems wasteful, there is also hidden waste like having to read-and-skip unwanted records to get to the one that you want, so my conclusion is that you should set Max to as large a value as you might conceivably need. 

My use of Max=6 is NOT a balance between excessively large messages and other overheads. 6 was chosen because my priority was developing code that would silently handle the situation where Max was not large enough to get all the records.  For a real situation with data like mine I’d have set Max=14.   Similarly, with program JSPG3 I set the number of child records to 10, even though I had ensured that for DEPTNO=A00 there were 30 EMPLOYEE records. In a real situation I would have set the number to 30.

Logic used when Max > 1

When Max > 1 then the interface saves the first n records in a list. Thus the first WorkDept=D11 Enquiry saved the first 6 records.  On reading the 7th record the next 6 records are added to this list, and so on until the last record (known from BrowseCount) is read.   Backward scrolling ([<] and [<<]) is always instant, while forward scrolling ([>]) is instant except for every 6th record.  Scroll to end ([>>]) might use several request/responses.

The client’s record list is maintained until there is another [Enquiry].  If a record is updated then the updated record replaces the original record in the list.  Added records are added to the list and BrowseCount incremented.  Deleted records are deleted from the list and Browsecount reduced, but the deleted record remains displayed until the next scrolling command or [Enquiry].

In the following appendices you’ll see significant differences in JSPG2Client/JSPG2AClient logic, but none in JSPG2Test/JSPG2ATest.

Measuring Response Time

I measured response times by adding code like this to the interface.  If relevant, you should do the same to check out your situation: -

JSPG2Client and JSPG2AClient.Scroll.

        private bool Scroll(int Increment, string PSkip = null,string PWORKDEPT = null)

        {

            Stopwatch stopwatch = new Stopwatch();

            stopwatch.Start();

            stopwatch.Stop();

            Debug.Print("Scroll Stopwatch: ms=" + stopwatch.ElapsedMilliseconds.ToString());

            return DoRead(PSkip, "", PWORKDEPT);

        }

JSPG2Client and JSPG2AClient.DoRead.  It was necessary to change the generated statement
           
return = RequestResponse();

to         bool Result = RequestResponse();

so that the Stop time could be reported.

        public bool DoRead(string PSkip = null,string PEMPNO = null,string PWORKDEPT = null)

        {

            Stopwatch stopwatch = new Stopwatch();

            stopwatch.Start();

            bool Result = RequestResponse();

            stopwatch.Stop();

            Debug.Print("RequestResponse Stopwatch: ms=" + stopwatch.ElapsedMilliseconds.ToString());

            return Result;

        }

Browsecount

Browsecount is calculated differently for SQL and for VSAM.   For SQL it is always accurate, because the SQL logic starts with SELECT COUNT(*), e.g.

005440         SELECT COUNT(*) INTO :OJSPG1.JZ-Employee-BrowseCount FROMJSPG1

005450              EMPLOYEE WHERE WORKDEPT LIKE :JZLIKE                JSPG1

There is no equivalent of COUNT(*) with VSAM.  Since the point of BrowseCount is to control scrolling, and for this purpose it is sufficient if it is greater than the number read unless all the browsed records have been read, the value of BrowseCount for VSAM is : -
            If the browse list has been read to the end, BrowseCount is accurate.
            If the browse list has not been read completely, BrowseCount is 1 greater than the number of records that have been read.

Example:  Program JSPG1V is like program JSPG1, but instead of reading data from DB2 table EMPLOYEE it reads a VSAM file CustF.  JSPG1V was generated with Max=6, so: -

Enter Name = Barnes and click [Read].  The first 6 records are read, and BrowseCount = 7: -

Scrolling forward [>] is instant until the 7th record is reached when the next 6 records are read, and BrowseCount becomes 13.

Last record, [>>], read another 6 records, and so might need to be clicked several times to really get to the end.  With my test data this browse returns 23 records, so three [>>] clicks are needed to get to the end.

This logic was designed for VSAM to avoid the need to read all the VSAM records with every browse enquiry just to calculate BrowseCount accurately.

Jazz Message Definitions

This is the message definition for program JSPG2A.  Note that it contains three main parts, but also refers to other definitions through COPY statements.  To deduce the full data rules, you need to follow the Assign relationships, for example to understand that IJSPG2A.EMPNO must be numeric in the range 1-999999 you need to look up EMPLOYEE.EMPNO.   This definition gives Jazz all the information that it needs to create the interface. 

*# Last Updated by JAZZUSR at 14/11/2021 2:43:27 PM

*# You may edit this definition: right-click the 'WEBSERVICE' keyword of the PROGRAM statement.

COPY EMPLOYEE;

COPY TYPES;

DEFINE MyJSv-JSPG2A SYSTEM DATA([Not in COBOL

    INPUT VARCHAR(7) VALUE 'IJSPG2A',

    OUTPUT VARCHAR(7) VALUE 'OJSPG2A',

    MType CHAR(4) VALUE 'JSON',

    Template CHAR(8) VALUE '@JSF1Upd',

    URL VARCHAR(42) VALUE 'http://localhost:9003/cics/services/JSPG2A');

DEFINE IJSPG2A SERVICE INPUT DATA(

    Function CHAR(1) CAPS CODES(E:Enquiry,U:Update,A:Add,D:Delete)  VALUE Enquiry,

    JZ-EMPLOYEE-Skip SMALLINT VALUE 0 RANGE(0:999),

    JZ-EMPLOYEE GROUP,

        EMPNO CHAR(6) ASSIGN EMPLOYEE.EMPNO,

        FIRSTNME CHAR(12) ASSIGN EMPLOYEE.FIRSTNME,

        MIDINIT CHAR(1) ASSIGN EMPLOYEE.MIDINIT,

        LASTNAME CHAR(15) ASSIGN EMPLOYEE.LASTNAME,

        WORKDEPT CHAR(3) ASSIGN EMPLOYEE.WORKDEPT,

        PHONENO CHAR(4) ASSIGN EMPLOYEE.PHONENO,

        HIREDATE CHAR(9) ASSIGN EMPLOYEE.HIREDATE,

        JOB CHAR(8) ASSIGN EMPLOYEE.JOB,

        EDLEVEL CHAR(7) ASSIGN EMPLOYEE.EDLEVEL,

        SEX CHAR(1) ASSIGN EMPLOYEE.SEX,

        BIRTHDATE CHAR(9) ASSIGN EMPLOYEE.BIRTHDATE,

        SALARY CHAR(15) ASSIGN EMPLOYEE.SALARY,

        BONUS CHAR(15) ASSIGN EMPLOYEE.BONUS,

        COMM CHAR(15) ASSIGN EMPLOYEE.COMM,

        CURRENCY CHAR(3) ASSIGN EMPLOYEE.CURRENCY,

        DEPTMGR CHAR(5) ASSIGN EMPLOYEE.DEPTMGR,

        Checksum CHAR(40),

        END GROUP);

DEFINE OJSPG2A SERVICE OUTPUT DATA(

    Error VARCHAR(80),

    JZ-EMPLOYEE-ReadTo SMALLINT VALUE 0,

    JZ-EMPLOYEE-NbrReturned SMALLINT VALUE 0,

    JZ-EMPLOYEE-BrowseCount SMALLINT VALUE 0,

    JZ-EMPLOYEE (6) GROUP,

        JZ-EMPLOYEE-ReturnCode LIKE Types.ReturnCode,

        EMPNO LIKE EMPLOYEE.EMPNO ASSIGN EMPLOYEE.EMPNO,

        FIRSTNME LIKE EMPLOYEE.FIRSTNME ASSIGN EMPLOYEE.FIRSTNME,

        MIDINIT LIKE EMPLOYEE.MIDINIT ASSIGN EMPLOYEE.MIDINIT,

        LASTNAME LIKE EMPLOYEE.LASTNAME ASSIGN EMPLOYEE.LASTNAME,

        WORKDEPT LIKE EMPLOYEE.WORKDEPT ASSIGN EMPLOYEE.WORKDEPT,

        PHONENO LIKE EMPLOYEE.PHONENO ASSIGN EMPLOYEE.PHONENO,

        HIREDATE LIKE EMPLOYEE.HIREDATE ASSIGN EMPLOYEE.HIREDATE,

        JOB LIKE EMPLOYEE.JOB ASSIGN EMPLOYEE.JOB,

        EDLEVEL LIKE EMPLOYEE.EDLEVEL ASSIGN EMPLOYEE.EDLEVEL,

        SEX LIKE EMPLOYEE.SEX ASSIGN EMPLOYEE.SEX,

        BIRTHDATE LIKE EMPLOYEE.BIRTHDATE ASSIGN EMPLOYEE.BIRTHDATE,

        SALARY LIKE EMPLOYEE.SALARY ASSIGN EMPLOYEE.SALARY,

        BONUS LIKE EMPLOYEE.BONUS ASSIGN EMPLOYEE.BONUS,

        COMM LIKE EMPLOYEE.COMM ASSIGN EMPLOYEE.COMM,

        CURRENCY LIKE EMPLOYEE.CURRENCY ASSIGN EMPLOYEE.CURRENCY,

        DEPTMGR LIKE EMPLOYEE.DEPTMGR ASSIGN EMPLOYEE.DEPTMGR,

        Checksum CHAR(40),

        END GROUP);

Note that the output record is defined with a dimension, even for program JSPG1.  This value corresponds to the Max value given for Employee when the service was generated, so for JSPG2A it is 6.  For JSPG2 this would be 1.

 

Here is the definition of the EMPLOYEE record, referenced in LIKE and ASSIGN properties: -

*# Last Updated by JAZZUSR at 31/08/2020 1:33:28 PM

*# Created from Table:EMPLOYEE, Schema:ROBERTBW10, Database:Sample by JazzUser at 26/03/2019 4:25:46 PM

COPY Department;

DEFINE EMPLOYEE SQL DATA(   

    EMPNO CHAR(6) PIC '999999' REQUIRED KEY,

    FIRSTNME VARCHAR(12) REQUIRED,

    MIDINIT CHAR (1),

    LASTNAME VARCHAR(15) REQUIRED,

    WORKDEPT CHAR(3) CAPS DKEY 'XEMP2' EXISTS DEPARTMENT.DEPTNO,

    PHONENO CHAR(4) PIC '9999',

    HIREDATE DATE,

    JOB CHAR(8),

    EDLEVEL SMALLINT REQUIRED,

    SEX CHAR(1) CAPS CODES(M:Male, F:Female),

    BIRTHDATE DATE,

    SALARY MONEY(9,2) MIN 0,

    BONUS MONEY(9,2) MIN 0,

    COMM MONEY(9,2) MIN 0,

    CURRENCY CHAR(3),

    DEPTMGR BOOLEAN);

Message and Field Names

A Jazz container definition like that above has a standardized appearance.  It contains three DEFINE statements, and it may reference other definitions directly or indirectly through COPY statements.  The first definition,

DEFINE MyJSv-JSPG2 SYSTEM DATA([Not in COBOL

is not generated into the COBOL (because it has type SYSTEM) but it gives vital information to the [Client] generation, including the names of the input and output messages.  The name of this definition is always Service-Program.  The input message will be named by prefixing the program name with “I”, and the output message will use prefix “O”.

Most of the field names in the input and output messages are user-defined.  In the messages above, most of the fields have come from the EMPLOYEE record, and use the same names as there.  However there are a few other fields with particular names.  Most have the form JZ-xxxx-Field, e.g. JZ-Employee-Skip which is a field used to control browsing.  Jazz will not permit you to name your own fields starting with JZ.  There are a few fields that do not follow this form: do not use these as your own field names in web services: -

FUNCTION     This is an input field defined by Jazz logic.
ERROR           An output field that may contain an error message
CheckSum      Both Input and Output: this is used to manage pseudo-locking ensuring update integrity.

If the message doesn’t contain ERROR and ReturnCode (here with the form JZ-xxxx-ReturnCode) , they will be generated into the client anyway as they are used by the client to report local validation issues. 

Messages – COBOL, JSON, C#, and Binding

We wrote a web service program in MANASYS Jazz, and when we clicked [Process] this was turned into COBOL.  The COBOL program will contain data definitions based on the Jazz-format definitions above.  For example, this is the request message, IJSPG2: -

003360 01  IJSPG2.                                                      JSPG2

003370      03 JZ-Function PIC X VALUE 'E'.                             JSPG2

003380      03 JZ-Employee-Skip PIC S9(4) COMP-5 VALUE 0.               JSPG2

003390      03 JZ-Employee.                                             JSPG2

003400        05 EMPNO PIC X(6) VALUE SPACES.                           JSPG2

003410        05 FIRSTNME PIC X(12) VALUE SPACES.                       JSPG2

003420        05 MIDINIT PIC X VALUE SPACES.                            JSPG2

003430        05 LASTNAME PIC X(15) VALUE SPACES.                       JSPG2

003440        05 WORKDEPT PIC XXX VALUE SPACES.                         JSPG2

003450        05 PHONENO PIC XXXX VALUE SPACES.                         JSPG2

003460        05 HIREDATE PIC X(9) VALUE SPACES.                        JSPG2

003470        05 JOB PIC X(8) VALUE SPACES.                             JSPG2

003480        05 EDLEVEL PIC X(7) VALUE SPACES.                         JSPG2

003490        05 SEX PIC X VALUE SPACES.                                JSPG2

003500        05 BIRTHDATE PIC X(9) VALUE SPACES.                       JSPG2

003510        05 SALARY PIC X(15) VALUE SPACES.                         JSPG2

003520        05 BONUS PIC X(15) VALUE SPACES.                          JSPG2

003530        05 COMM PIC X(15) VALUE SPACES.                           JSPG2

003540        05 JZ-CURRENCY PIC XXX VALUE SPACES.                      JSPG2

003550        05 DEPTMGR PIC XXXXX VALUE SPACES.                        JSPG2

003560        05 Checksum PIC X(40) VALUE SPACES.                       JSPG2

To get the request message the COBOL program contains

005700     EXEC CICS                                                    JSPG2

005710         GET CONTAINER(JZInputContainer) INTO(IJSPG2)             JSPG2

005720             RESP(JZ-RESPONSE)                                    JSPG2

005730     END-EXEC.                                                    JSPG2

Similarly, there will be a COBOL definition of the response, which will be sent back to the client with

021250     EXEC CICS                                                    JSPG2

021260         PUT CONTAINER(JZContainerName) FROM(OJSPG2)              JSPG2

021270             RESP(JZ-RESPONSE)                                    JSPG2

021280     END-EXEC.                                                    JSPG2

But the client is not COBOL!  The messages must be converted to a standard form that can be understood, whatever the language the client uses.  For this to happen, after program JSPG2 was compiled a “binding object” is produced to convert the messages between COBOL-format and JSON, the most popular standard form, and JSON descriptions of the request and response messages were produced.

With Jazz configured for z/OS, this all happens on the mainframe as a result of JCL steps that are automatically inserted into the job that compiles the COBOL.  With Jazz configured to work with Micro Focus Enterprise Developer, this is done through the dialog described here.

Request message as JSON.

This is the JSON generated from the COBOL for the request message

{

  "$schema" : "http:\/\/json-schema.org\/draft-04\/schema#",

  "title" : "JSPG2",

  "description" : "Micro Focus Enterprise Developer - This generated JSON schema document is provided 'as is' and without any warranties of any kind, and may be used by licensee solely for the purposes of describing a REST API. Micro Focus shall not be responsible for and hereby disclaims any liabilities that may result from licensee's use of or modifications to this generated JSON schema document.",

  "type" : "object",

  "properties" :

  {

    "JSPG2" :

    {

      "type" : "object",

      "properties" :

      {

        "IJSPG2" :

        {

          "type" : "object",

          "properties" :

          {

            "JZ_Function" :

            {

              "type" : "string",

              "maxLength" : 1

            },

            "JZ_Employee_Skip" :

            {

              "type" : "integer"

            },

            "JZ_Employee" :

            {

              "type" : "object",

              "properties" :

              {

                "EMPNO" :

                {

                  "type" : "string",

                  "maxLength" : 6

                },

                "FIRSTNME" :

                {

                  "type" : "string",

                  "maxLength" : 12

                },

                "MIDINIT" :

                {

                  "type" : "string",

                  "maxLength" : 1

                },

                "LASTNAME" :

                {

                  "type" : "string",

                  "maxLength" : 15

                },

                "WORKDEPT" :

                {

                  "type" : "string",

                  "maxLength" : 3

                },

                "PHONENO" :

                {

                  "type" : "string",

                  "maxLength" : 4

                },

                "HIREDATE" :

                {

                  "type" : "string",

                  "maxLength" : 9

                },

                "JOB" :

                {

                  "type" : "string",

                  "maxLength" : 8

                },

                "EDLEVEL" :

                {

                  "type" : "string",

                  "maxLength" : 7

                },

                "SEX" :

                {

                  "type" : "string",

                  "maxLength" : 1

                },

                "BIRTHDATE" :

                {

                  "type" : "string",

                  "maxLength" : 9

                },

                "SALARY" :

                {

                  "type" : "string",

                  "maxLength" : 15

                },

                "BONUS" :

                {

                  "type" : "string",

                  "maxLength" : 15

                },

                "COMM" :

                {

                  "type" : "string",

                  "maxLength" : 15

                },

                "JZ_CURRENCY" :

                {

                  "type" : "string",

                  "maxLength" : 3

                },

                "DEPTMGR" :

                {

                  "type" : "string",

                  "maxLength" : 5

                },

                "Checksum" :

                {

                  "type" : "string",

                  "maxLength" : 40

                }

              }

            }

          }

        }

      }

    }

  }

}

RequestJSPG2.cs

Just as we use COBOL definitions within the web server program JSPG2, within the C# program JSPG2Client we convert the JSON into a hierarchy of C# classes.  Here is the request definition, as it will be used within JSPG2Client.  Except for naming, there is no difference between this and RequestJSPG2A.cs

namespace MyJSv

{

    public class RequestJSPG2

    {

        public class JSPG2_

        {

            public class IJSPG2_

            {

                public string JZ_Function { get; set; }

                public int JZ_Employee_Skip { get; set; }

                public class JZ_Employee_

                {

                    public string EMPNO { get; set; }

                    public string FIRSTNME { get; set; }

                    public string MIDINIT { get; set; }

                    public string LASTNAME { get; set; }

                    public string WORKDEPT { get; set; }

                    public string PHONENO { get; set; }

                    public string HIREDATE { get; set; }

                    public string JOB { get; set; }

                    public string EDLEVEL { get; set; }

                    public string SEX { get; set; }

                    public string BIRTHDATE { get; set; }

                    public string SALARY { get; set; }

                    public string BONUS { get; set; }

                    public string COMM { get; set; }

                    public string JZ_CURRENCY { get; set; }

                    public string Checksum { get; set; }

                }

                public JZ_Employee_ JZ_Employee { get; } = new JZ_Employee_();

            }

            public IJSPG2_ IJSPG2 { get; } = new IJSPG2_();

        }

        public JSPG2_ JSPG2 { get; } = new JSPG2_();

    }

}

Note the way in which the lower-level classes are made addressable by naming the original class with a _, e.g. JSPG2_ and then following this class with

       public JSPG2_ JSPG2 { get; } = new JSPG2_();

From public class IJSPG2_ this corresponds directly to the JSON schema.  The outer two layers are added to ensure that the structure confirms to the JSON that is received, otherwise the JSON would needs to be text-edited on input and output before being serialised/de-serialised.   Statements can now be generated into JSPG2Client reflecting this structure, for example
           
Request.JSPG2.IJSPG2.JZ_Employee.EMPNO = _EMPNO;

ResponseJSPG2.cs

Like RequestJSPG2.CS, this maps the hierarchical structure of the response, and has a couple of levels at the top to reflect the JSON structure: -

namespace MyJSv

{

    public class ResponseJSPG2

    {

        public class JSPG2Response_

        {

            public class OJSPG2_

            {

                public class JZ_Error_

                {

                    public int JZL_Error { get; set; }

                    public string JZD_Error { get; set; }

                }

                public JZ_Error_ JZ_Error { get; } = new JZ_Error_();

                public int JZ_Employee_ReadTo { get; set; }

                public int JZ_Employee_NbrReturned { get; set; }

                public int JZ_Employee_BrowseCount { get; set; }

                public class JZ_Employee_

                {

                    public string JZ_Employee_ReturnCode { get; set; }

                    public string EMPNO { get; set; }

                    public class FIRSTNME_

                    {

                        public int JZL_FIRSTNME { get; set; }

                        public string JZD_FIRSTNME { get; set; }

                    }

                    public FIRSTNME_ FIRSTNME { get; } = new FIRSTNME_();

                    public string MIDINIT { get; set; }

                    public class LASTNAME_

                    {

                        public int JZL_LASTNAME { get; set; }

                        public string JZD_LASTNAME { get; set; }

                    }