反序列化后代对象
Deserializing descendent objects
我正在与基于 JSON 的 API 通信,我无法更改。它总是 returns 一个 Response 对象,里面有一个不同的 Result 对象。通常它看起来像这样:
{ "ver": "2.0", "result": { "code": 0 } }
对于某些命令,结果对象是 'grown' 通过添加额外的属性:
{ "ver": "2.0", "result": { "code": 0, "hostName": "sample", "hostPort": 5000 } }
我使用 Newtonsoft 属性定义对象如下:
内部 class RpcResponse
{
[JsonProperty(PropertyName = "ver")]
public 字符串版本 { 得到;放; }
[JsonProperty(PropertyName = "result")]
public RpcResponseResult 结果
{
得到;
放;
}
内部 class RpcResponseResult
{
[JsonProperty(PropertyName = "code")]
public int 代码 { get;放; }
}
内部 class RpcExtendedResponseResult: RpcResponseResult
{
[JsonProperty(PropertyName = "hostName")]
public 字符串主机名 { 得到;放; }
[JsonProperty(PropertyName = "hostPort")]
public int HostPort { 得到;放; }
但是当Response对象被反序列化时:
RpcResponse rspResponse = JsonConvert.DeserializeObject(rspString);
它的结果 属性 总是显示为一个 RpcResponseResult 对象,即。 JsonConvert 不知道将其构造为 RpcExtendedResponseResult 对象。
有没有什么方法可以使用属性或转换器来恢复正确的后代对象?我觉得我错过了一些明显的东西!
因为对象的类型是RpcResponseResult。反序列化器只能反序列化以指定字段类型声明的字段。它无法确定,因为 class 有 "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;}
}
然后如果你真的想得到你想要的对象,你可以在你的容器中做这样的事情class:
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 };
}
首先,感谢 Matthew Frontino 提供了我接受的唯一答案。
但是我选择不制作单个结果容器,所以这就是我最终所做的。
- 首先我从这个页面开始:How to implement custom JsonConverter in JSON.NET to deserialize a List of base class objects?
- 我使用了 Alain 提供的 JsonCreationConverter 版本。
我按照 Dribbel 的建议添加了 CanWrite 覆盖:
public override bool CanWrite
{
get { return false; }
}
我还向 JsonCreationConverter 添加了自己的辅助函数:
protected bool FieldExists(string fieldName, JObject jObject) {
return jObject[fieldName] != null;
}
然后我创建了自己的转换器如下:
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();
}
}
然后我反序列化顶层 class 并提供要使用的任何转换器。在这种情况下,我只提供了一个,用于有问题的嵌套 class:
RpcResponse rspResponse = JsonConvert.DeserializeObject<RpcResponse>(
rspString,
new JsonSerializerSettings {
DateParseHandling = Newtonsoft.Json.DateParseHandling.None,
Converters = new List<JsonConverter>( new JsonConverter[] {
new RpcResponseResultConverter()
})
});
备注:
- 任何未由转换器显式处理的内容(例如顶级 class)都使用 JsonConvert 中内置的默认转换器进行反序列化。
- 只有当您可以为每个后代识别一组唯一的字段时,这才有效 class。
我正在与基于 JSON 的 API 通信,我无法更改。它总是 returns 一个 Response 对象,里面有一个不同的 Result 对象。通常它看起来像这样:
{ "ver": "2.0", "result": { "code": 0 } }
对于某些命令,结果对象是 'grown' 通过添加额外的属性:
{ "ver": "2.0", "result": { "code": 0, "hostName": "sample", "hostPort": 5000 } }
我使用 Newtonsoft 属性定义对象如下:
内部 class RpcResponse { [JsonProperty(PropertyName = "ver")] public 字符串版本 { 得到;放; } [JsonProperty(PropertyName = "result")] public RpcResponseResult 结果 { 得到; 放; } 内部 class RpcResponseResult { [JsonProperty(PropertyName = "code")] public int 代码 { get;放; } } 内部 class RpcExtendedResponseResult: RpcResponseResult { [JsonProperty(PropertyName = "hostName")] public 字符串主机名 { 得到;放; } [JsonProperty(PropertyName = "hostPort")] public int HostPort { 得到;放; }
但是当Response对象被反序列化时:
RpcResponse rspResponse = JsonConvert.DeserializeObject(rspString);
它的结果 属性 总是显示为一个 RpcResponseResult 对象,即。 JsonConvert 不知道将其构造为 RpcExtendedResponseResult 对象。
有没有什么方法可以使用属性或转换器来恢复正确的后代对象?我觉得我错过了一些明显的东西!
因为对象的类型是RpcResponseResult。反序列化器只能反序列化以指定字段类型声明的字段。它无法确定,因为 class 有 "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;}
}
然后如果你真的想得到你想要的对象,你可以在你的容器中做这样的事情class:
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 };
}
首先,感谢 Matthew Frontino 提供了我接受的唯一答案。
但是我选择不制作单个结果容器,所以这就是我最终所做的。
- 首先我从这个页面开始:How to implement custom JsonConverter in JSON.NET to deserialize a List of base class objects?
- 我使用了 Alain 提供的 JsonCreationConverter 版本。
我按照 Dribbel 的建议添加了 CanWrite 覆盖:
public override bool CanWrite { get { return false; } }
我还向 JsonCreationConverter 添加了自己的辅助函数:
protected bool FieldExists(string fieldName, JObject jObject) { return jObject[fieldName] != null; }
然后我创建了自己的转换器如下:
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(); } }
然后我反序列化顶层 class 并提供要使用的任何转换器。在这种情况下,我只提供了一个,用于有问题的嵌套 class:
RpcResponse rspResponse = JsonConvert.DeserializeObject<RpcResponse>( rspString, new JsonSerializerSettings { DateParseHandling = Newtonsoft.Json.DateParseHandling.None, Converters = new List<JsonConverter>( new JsonConverter[] { new RpcResponseResultConverter() }) });
备注:
- 任何未由转换器显式处理的内容(例如顶级 class)都使用 JsonConvert 中内置的默认转换器进行反序列化。
- 只有当您可以为每个后代识别一组唯一的字段时,这才有效 class。