将 JSON 反序列化为自定义列表

问题描述:

我有这个 json:

var x = [
    [99,"abc","2dp",{"GroupNum": 0,"Total":[4, 1]}],
    [7,"x","date"],
    [60,"x","1dp",{"GroupNum": 1}],
    ...
]

存在以下规则(让 i 指代内部列表索引):

The following rules exist (let i refer to the inner list index):

  • x[i][0] - 必填项 - 始终为整数
  • x[i][1] - 必填项 - 总是一个字符串
  • x[i][2] - 必填项 - 总是一个字符串
  • x[i][3] - 可选项目 - 当它存在时,它具有以下规则:
    • x[i][3].GroupNum - 必填字段 - 始终为整数
    • x[i][3].Total - 可选字段 - 当它存在时,它是一个整数列表
    • x[i][0] - mandatory item - always an integer
    • x[i][1] - mandatory item - always a string
    • x[i][2] - mandatory item - always a string
    • x[i][3] - optional item - when it exists then it has the following rules:
      • x[i][3].GroupNum - mandatory field - always an integer
      • x[i][3].Total - optional field - when it exists then it is a list of integers

      所以我为这些规则创建了以下类:

      So I have created the following classes for these rules:

      public class ReportTemplateField : System.Collections.CollectionBase
      {
          public object this[int index]
          {
              get
              {
                  switch (index)
                  {
                      case 0:
                          return (int)List[index];
                      case 1:
                      case 2:
                          return (string)List[index];
                      case 3:
                          return (ReportGrouping)List[index];
                      default:
                          throw new System.ArgumentOutOfRangeException("Class ReportTemplateField only contains 4 items");
                  }
              }
              set
              {
                  switch (Count)
                  {
                      case 0:
                          List[index] = unchecked((int)value);
                          break;
                      case 1:
                      case 2:
                          List[index] = (string)value;
                          break;
                      case 3:
                          List[index] = (ReportGrouping)value;
                          break;
                      default:
                          throw new System.ArgumentOutOfRangeException("Class ReportTemplateField may only contain 4 items");
                  }
              }
          }
      
          public void Add(object item)
          {
              switch (Count)
              {
                  case 0:
                      List.Add(unchecked((int)item));
                      break;
                  case 1:
                  case 2:
                      List.Add((string)item);
                      break;
                  case 3:
                      List.Add((ReportGrouping)item);
                      break;
                  default:
                      throw new System.ArgumentOutOfRangeException("Class ReportTemplateField may only contain 4 items");
              }
          }
      }
      
      public class ReportGrouping
      {
          public int GroupNum { get; set; }
          public List<int> Total { get; set; }
      }
      

      然而,当我尝试使用 json.net 反序列化时,ReportTemplateField 类永远不会被使用.我知道这一点是因为我在每个方法中都设置了断点,但没有一个被触发:

      However, when I attempt to deserialize using json.net, the ReportTemplateField class never gets used. I know this because I have put breakpoints in every method and none of them get triggered:

      string json = "[[99,"abc","2dp",{"GroupNum": 0,"Total":[4, 1]}],[7,"x","date"],[60,"x","1dp",{"GroupNum": 1}]]";
      List<ReportTemplateField> parsed = JsonConvert.DeserializeObject<List<ReportTemplateField>>(json);
      int Total01 = parsed[0][3].Total[1];
      

      给我这个错误:

      错误 CS1061:对象"不包含总计"的定义和没有接受第一个类型参数的扩展方法Total"可以找到对象"(您是否缺少 using 指令或汇编参考?)

      error CS1061: 'object' does not contain a definition for 'Total' and no extension method 'Total' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?)

      但是,如果我只是单独反序列化 ReportGrouping,它会起作用:

      However it works if I just deserialize the ReportGrouping in isolation:

      string json2 = "{"GroupNum": 0,"Total":[4, 1]}";
      ReportGrouping parsed2 = JsonConvert.DeserializeObject<ReportGrouping>(json2);
      parsed2.Total[1]; // 1
      

      所以问题在于 ReportTemplateField 类.也许我不应该从 System.Collections.CollectionBase 继承?我基本上按照此演练进行了一些修改 - https://msdn.microsoft.com/en-us/library/xth2y6ft(v=vs.71).aspx 但它确实说文档可能已经过时.我使用的是 .NET 4.5,所以请将其纳入您的任何答案中.

      So the problem lies with the ReportTemplateField class. Maybe I shouldn't be inheriting from System.Collections.CollectionBase? I basically followed this walkthough with some modifications - https://msdn.microsoft.com/en-us/library/xth2y6ft(v=vs.71).aspx but it does say that the documentation may be outdated. I'm using .NET 4.5 so please factor that into any your answers.

      我怎样才能让 Json.net 正确反序列化,这样我就可以去 parsed[0][3].Total[1]; 访问第二个总数的整数值,而不需要需要投射它或对它做任何事情吗?

      How can I get Json.net to deserialize correctly, so that I can just go parsed[0][3].Total[1]; to access the integer value of the second total without needing to cast it or do anything to it at all?

代码中有几个错误,一是对象没有名为 Total 的属性,二是 parsed[0][3] 是一个字符串.

There is a few errors in the code, 1 is that object does not have a property called Total and another is that parsed[0][3] is a string.

您的代码中有一些错误:

You have a few errors in the code:

  1. parsed[0][3] 是一个字符串.通过将字符串转换为 ReportGrouping 来修复它.
  2. new ReportGrouping().GroupName 应根据 Json 命名为 Group.
  3. 在您将 parsed[0][3] 解析为 ReportingGroup 后,它仍会返回一个对象,因此之后将 i 转换为 ReportingGroup.IE.(ReportingGroup)parsed[0][3].

  1. parsed[0][3] is a string. Fix it by converting the string into a ReportGrouping.
  2. new ReportGrouping().GroupName should be named Group according to Json.
  3. After you have parsed parsed[0][3] into a ReportingGroup it will still return a object, so cast i into a ReportingGroup afterwards. I.e. (ReportingGroup)parsed[0][3].

get
{
    switch (index)
    {
        case 0:
            return (int)List[index];
        case 1:
        case 2:
            return (string)List[index];
        case 3:
            return JsonConvert.DeserializeObject<ReportGrouping>(List[index].ToString());
        default:
            throw new System.ArgumentOutOfRangeException("Class ReportTemplateField only contains 4 items");
    }
}

并像这样使用它:

List<ReportTemplateField> parsed = JsonConvert.DeserializeObject<List<ReportTemplateField>>(json);
ReportGrouping group = (ReportGrouping)parsed[0][3];

不要忘记将 GroupName 重命名为 Group 并修复 setter.

Dont forget to rename GroupName into Group and to fix the setter.

抱歉我重复了这么多次..

Sorry for repeating myself so many times..