1

Summary MANASYS Jazz generates CICS (= mainframe) Web services as COBOL programs that communicate using JSON, and it also generates related C# interfaces that expose these services to client programs as properties and methods. Program JSPG2 responds with a single employee record, JSPG2A is a version that can return up to 10 Employee records. JSPG2 works perfectly (see this video), but JSPG2A does not.

Detail To map hierarchical COBOL record structures to C# classes, a set of nested C# classes is defined like this

namespace MyJSv
{
    public class ResponseJSPG2A
    {
        public class JSPG2AResponse_
        {
            public class OJSPG2A_
            {
//...
                public int JZ_Employee_BrowseCount { get; set; }
                // and more scalar classes,
                public class JZ_Employee_
                {
                    public string JZ_Employee_NthReturnCode { get; set; }
                    public string EMPNO { get; set; }
                    // and more classes within the Employee record
                }
                public JZ_Employee_[] JZ_Employee { get; } = new JZ_Employee_[10];
            }
            public OJSPG2A_ OJSPG2A { get; } = new OJSPG2A_ ();
        }
        public JSPG2AResponse_ JSPG2AResponse { get; } = new JSPG2AResponse_ ();
    }
}

For a single occurrence of JZ-Employee its definition is

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

Otherwise the definition of ResponseJSPG2 and ResponseJSPG2A are the same

Newtonsoft.JSON converts the incoming JSON to C# with

   Response = JsonConvert.DeserializeObject<ResponseJSPG2>(result);

and then

   AssignResponseToProperties(true);

assigns data from ResponseJSPG2 to the properties that JSPG2Client wishes to expose.

This all works perfectly with ResponseJSPG2, where there is a single Employee record. The equivalent with <ResponseJSPG2A> where there is an array of 10 records returns 10 null records, and the equivalent AssignResponseToProperties fails when it attempts to reference to an Employee field: -

_EMPNO = Response.JSPG2AResponse.OJSPG2A.JZ_Employee[EmployeeSub].EMPNO;

Yet Visual Studio debugging, and the same test of the web service program JSPG2A with test utility ReadyAPI, shows that JSON is returned with 10 Employee records, all containing the expected data.

Here is the JSON returned to the test, edited to remove most fields and only 2 Employee records, so that it matches the nested C# classes above: -

{
  "JSPG2AResponse": {
    "OJSPG2A": {
      "JZ_Employee_BrowseCount": 16,
      "JZ_Employee": [
        {
          "JZ_Employee_NthReturnCode": "F",
          "EMPNO": "000060"
        },
        {
          "JZ_Employee_NthReturnCode": "N",
          "EMPNO": "000090"
        }
      ]
    }
  }
}

Question

Is there a way of getting DeserializeObject to handle array classes automatically, or do I have to use Newtonsoft.Json.Linq and write logic to parse the JSON and handle the array explicitly? Or some other way of achieving my objective?

dbc
  • 104,963
  • 20
  • 228
  • 340
RobertB_NZ
  • 13
  • 3
  • Does this answer your question? [How to handle both a single item and an array for the same property using JSON.net](https://stackoverflow.com/questions/18994685/how-to-handle-both-a-single-item-and-an-array-for-the-same-property-using-json-n) – Martin Nov 06 '20 at 23:57
  • I've added an edited version of the actual JSON (only 2 records, most of the fields omitted) so that it matches the C# class structure(data model) that I'd given. The deserialization code was also already given. You don't want the full request/response code as this would also require anybody duplicating my actual program to set up a CICS Web Server and DB2 database. – RobertB_NZ Nov 06 '20 at 23:57
  • Thanks Martin, I'll check these out and I'll get back either to say "All OK" or with further questions". – RobertB_NZ Nov 07 '20 at 00:01
  • @RobertB_NZ - For the future, this question has a lot of extra detail that wasn't really needed. What was really needed was a [mcve] -- just enough code and JSON to demonstrate the problem. I've created one here: https://dotnetfiddle.net/YDfEvf. – dbc Nov 07 '20 at 00:56

1 Answers1

0

Your problem is that the JZ_Employee property is an array-valued property that lacks a setter:

public JZ_Employee_[] JZ_Employee { get; } = new JZ_Employee_[10];

Because .Net arrays cannot be resized, Json.NET treats them as read-only collections whose contents need to be accumulated into a temporary list which is then used to construct the array and set it back in its parent. However, your array property has no setter, so Json.NET sees it as completely read-only and skips it entirely.

To resolve this problem, you could add a setter. It could be protected or private if you mark the property with [JsonProperty]:

[JsonProperty]
public JZ_Employee_[] JZ_Employee { get; private set; } = new JZ_Employee_[10];

Demo fiddle #1 here.

Alternatively, you could leave the property as get-only but replace the array with a resizable collection such as List<JZ_Employee_>:

public List<JZ_Employee_> JZ_Employee { get; } = new List<JZ_Employee_>();

Demo fiddle #2 here.

This second option has the advantage that you can apply [JsonConverter(typeof(SingleOrArrayConverter<JZ_Employee_>))] or just [JsonConverter(typeof(SingleOrArrayListConverter))] from the answers to How to handle both a single item and an array for the same property using JSON.net to JZ_Employee, which will allow you to merge the ResponseJSPG2 and ResponseJSPG2A classes:

[JsonConverter(typeof(SingleOrArrayConverter<JZ_Employee_>))]
public List<JZ_Employee_> JZ_Employee { get; } = new List<JZ_Employee_>();

The converter will automatically convert the case of a single JZ_Employee object into a list with one item.

dbc
  • 104,963
  • 20
  • 228
  • 340
  • Excellent, this offers a very simple fix to my problem. I've gone with the second option. Thank you, Robert. – RobertB_NZ Nov 08 '20 at 00:51