首页 > 解决方案 > 使用包含数组的嵌套类进行 JSON 到 C# 的转换

问题描述

总结 MANASYS Jazz 将CICS(= 大型机)Web 服务生成为使用 JSON 进行通信的 COBOL 程序,它还生成相关的 C# 接口,将这些服务作为属性和方法公开给客户端程序。程序 JSPG2 以单个员工记录响应,JSPG2A 是一个最多可以返回 10 条员工记录的版本。JSPG2 完美运行(请参阅此视频),但 JSPG2A 不能。

详细信息 为了将分层 COBOL 记录结构映射到 C# 类,一组嵌套的 C# 类定义如下

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_ ();
    }
}

对于 JZ-Employee 的一次出现,它的定义是

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

否则 ResponseJSPG2 和 ResponseJSPG2A 的定义是一样的

Newtonsoft.JSON 将传入的 JSON 转换为 C#

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

进而

   AssignResponseToProperties(true);

将 ResponseJSPG2 中的数据分配给 JSPG2Client 希望公开的属性。

这一切都与 完美配合ResponseJSPG2,其中有一个员工记录。与<ResponseJSPG2A>where 有 10 条记录的数组的等效项返回 10 条空记录,而等效项AssignResponseToProperties在尝试引用 Employee 字段时失败:-

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

然而,Visual Studio 调试以及使用测试实用程序 ReadyAPI 对 Web 服务程序 JSPG2A 进行的相同测试表明,返回的 JSON 包含 10 条员工记录,所有记录都包含预期的数据。

这是返回给测试的 JSON,经过编辑以删除大部分字段和仅 2 条员工记录,以便它与上面的嵌套 C# 类匹配:-

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

问题

有没有办法让 DeserializeObject 自动处理数组类,还是我必须使用 Newtonsoft.Json.Linq 并编写逻辑来解析 JSON 并显式处理数组?或者其他实现我目标的方法?

标签: c#jsonjson.net

解决方案


您的问题是该JZ_Employee属性是一个缺少 setter的数组值属性:

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

因为 .Net 数组无法调整大小,Json.NET 将它们视为只读集合,其内容需要累积到临时列表中,然后用于构造数组并将其设置回其父级。但是,您的数组属性没有设置器,因此 Json.NET 将其视为完全只读并完全跳过它。

要解决此问题,您可以添加一个 setter。可能是protected或者private如果您使用以下标记属性[JsonProperty]

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

演示小提琴#1在这里

或者,您可以将属性保留为 get-only,但将数组替换为可调整大小的集合,例如List<JZ_Employee_>

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

演示小提琴#2在这里

第二个选项的优点是您可以应用[JsonConverter(typeof(SingleOrArrayConverter<JZ_Employee_>))]或仅从如何使用 JSON.net to处理同一属性的单个项目和数组的[JsonConverter(typeof(SingleOrArrayListConverter))]答案中应用,这将允许您合并和类:JZ_EmployeeResponseJSPG2ResponseJSPG2A

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

转换器会自动将单个JZ_Employee对象的大小写转换为包含一项的列表。


推荐阅读