【问题标题】:Deserializing descendent objects反序列化后代对象
【发布时间】:2023-04-04 08:10:02
【问题描述】:

我正在使用无法更改的基于 JSON 的 API 进行通信。它总是返回一个 Response 对象,其中包含一个不同的 Result 对象。通常它看起来像这样:

{ "ver": "2.0", "result": { "code": 0 } }

对于某些命令,Result 对象是通过添加额外属性来“增长”的:

{ "ver": "2.0", "result": { "code": 0, "hostName": "sample", "hostPort": 5000 } }

我使用 Newtonsoft 属性来定义对象如下:

内部类 RpcResponse { [JsonProperty(PropertyName = "ver")] 公共字符串版本{获取;放; } [JsonProperty(PropertyName = "结果")] 公共 RpcResponseResult 结果 { 得到; 放; } 内部类 RpcResponseResult { [JsonProperty(PropertyName = "代码")] 公共 int 代码 { 获取;放; } } 内部类 RpcExtendedResponseResult: RpcResponseResult { [JsonProperty(PropertyName = "hostName")] 公共字符串主机名 { 获取;放; } [JsonProperty(PropertyName = "hostPort")] 公共 int 主机端口 { 获取;放; }

但是当Response对象被反序列化时:

RpcResponse rspResponse = JsonConvert.DeserializeObject(rspString);

其 Result 属性始终显示为 RpcResponseResult 对象,即。 JsonConvert 不知道将其构造为 RpcExtendedResponseResult 对象。

属性或转换器有什么方法可以恢复正确的后代对象吗?我觉得我错过了一些明显的东西!

【问题讨论】:

标签: c# json.net


【解决方案1】:

因为对象的类型是RpcResponseResult。反序列化器只能反序列化以指定字段类型声明的字段。它无法确定,因为一个类有“hostName”,它现在是一个 RpcExtendedResponseResult。

如果我这样做,我可能会根据需要将结果作为所有可能字段的容器并使用默认值,然后您可以根据需要填充另一个对象。

internal class RpcResponseResultContainer
{
    [JsonProperty(PropertyName = "code")]
    public int Code { get; set; }

    [JsonProperty(PropertyName = "hostName")]
    private string mHostName = string.Empty;
    public string HostName 
    { 
       get { return mHostName;} 
       set { mHostName = value; }
    }

    [JsonProperty(PropertyName = "hostPort")]
    private int mHostPort = -1;
    public int HostPort 
    { 
       get { return mHostPort;} 
       set { mHostPort = value;}
    }

如果你真的想要得到你想要的对象,你可以在你的容器类中做这样的事情:

 public RpcResponseResult GetActualResponseType()
 {
     if(HostPort != -1 && !string.IsNullOrEmtpy(HostName))
     {
         return new RpcExtendedResponseResult() { Code = this.Code, HostName = this.HostName, HostPort = this.HostPort};
     }
     return new RpcResponseResult() { Code = this.Code };
 }

【讨论】:

    【解决方案2】:

    首先,感谢 Matthew Frontino 提供了我接受的唯一答案。

    但是我选择不制作单个结果容器,所以这就是我最终要做的。

    1. 首先我从这个页面开始:How to implement custom JsonConverter in JSON.NET to deserialize a List of base class objects?
    2. 我使用了 Alain 提供的 JsonCreationConverter 版本。
    3. 我按照 Dribbel 的建议添加了 CanWrite 覆盖:

      public override bool CanWrite
      {
          get { return false; }
      }
      
    4. 我还在 JsonCreationConverter 中添加了自己的辅助函数:

      protected bool FieldExists(string fieldName, JObject jObject)    {
          return jObject[fieldName] != null;
      }
      
    5. 然后我创建了自己的转换器,如下所示:

      class RpcResponseResultConverter : JsonCreationConverter<RpcResponseResult>
      {
          protected override RpcResponseResult Create(Type objectType, JObject jObject)
          {
              // determine extended responses
              if (FieldExists("hostName", jObject) &&
                  FieldExists("hostPort", jObject) )
              {
                  return new RpcExtendedResponseResult();
              }
              //default
              return new RpcResponseResult();
          }
      }
      
    6. 然后我反序列化顶级类并提供要使用的任何转换器。在这种情况下,我只提供了一个,用于有问题的嵌套类:

      RpcResponse rspResponse = JsonConvert.DeserializeObject<RpcResponse>(
          rspString, 
          new JsonSerializerSettings {
              DateParseHandling = Newtonsoft.Json.DateParseHandling.None,
              Converters = new List<JsonConverter>( new JsonConverter[] {
                      new RpcResponseResultConverter()
                      })
              });
      

    注意事项:

    • 转换器未明确处理的任何内容(例如顶级类)都将使用 JsonConvert 中内置的默认转换器进行反序列化。
    • 仅当您可以为每个后代类识别一组唯一的字段时,这才有效。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-15
      • 1970-01-01
      • 1970-01-01
      • 2018-12-26
      相关资源
      最近更新 更多