【问题标题】:Using JsonConvert.DeserializeObject to dynamically choose class使用 JsonConvert.DeserializeObject 动态选择类
【发布时间】:2020-10-29 02:34:39
【问题描述】:

所以我正在进行 api 调用,我需要使用 JsonConvert.DeserializeObject 将其转换为类。 Json 结构返回如下

{
    "fcResponse": {
        "responseData": {
            "fcRequest": {
                "mail": "Emails",
                "outlookMail": "Outlook Emails",
                 (etc.)
            }
        }
    }
}

问题在于“fcRequest”中返回的值会根据我发送的参数而有所不同。

目前类结构如下

    public class GetSubModulesResponse : BaseResponse
    {
        [JsonProperty("fcResponse")]
        public SubModuleResponse Response { get; set; }
    }

    public class SubModuleResponse
    {
        [JsonProperty("responseData")]
        public SubModuleData Data { get; set; }
    }

    public class SubModuleData
    {
        [JsonProperty("fcRequest")]
        public SubModuleFIMRequest RequestFIM { get; set; }

        [JsonProperty("fcRequest")]
        public SubModuleFSRequest RequestFS { get; set; }
    }

这是基本的调用结构

GetSubModulesResponse subModuleResponse = new GetSubModulesResponse();
var response = SubmitAPICall();
subModuleResponse = JsonConvert.DeserializeObject<GetSubModulesResponse>(response);

现在我知道我显然不能在 RequestFIM 和 RequestFS 上拥有相同的 JsonProperty,但我正在尝试做的是找到一种方法来根据变量切换我应该使用这两个属性中的哪一个。

【问题讨论】:

  • 那么您的fcRequest 可以是两种不同类型之一?
  • 您正在处理的 API 是否有 SDK?大多数时候他们都会这样做。
  • @PatrickMcvay 不是我所知道的,我目前的结构是 fcRequest 以两种不同的类型返回,但我不知道这是否是最好的方法
  • 这是您构建的 API 吗?
  • 我会说这不是最好的方法。这将需要太多的猜测工作,而且只是在问问题。如果您可以控制的话,您可能应该每次都返回这两种类型,并将其中一种设为 null。

标签: c# serialization jsonconvert


【解决方案1】:

一种选择是为元素使用自定义(反)序列化程序。这样,您仍然可以在大多数情况下从自动反序列化中受益最少,并在需要的地方获得灵活性。我假设您使用的是 Newtonsoft JSON / JSON.NET。

让我们首先为fcRequest 元素引入一个基类。

public enum ResponseType
{
    FIM, FS
}

public abstract class ResponseBase
{
    [JsonIgnore]
    public abstract ResponseType ResponseType { get; }
}

通过在此处添加ResponseType 可以简化使用代码;如果你可以使用基于类型的模式匹配,你甚至可能不需要它。

我显然不知道您的域实体是什么,但为了争论,SubModuleFIMRequest 现在将包含邮件地址。另外,它还来源于说的ResponseBase

public class SubModuleFIMRequest : ResponseBase
{
    public override ResponseType ResponseType => ResponseType.FIM;

    [JsonProperty("mail")]
    public string Mail { get; set; }

    [JsonProperty("outlookMail")]
    public string OutlookMail { get; set; }
}

接下来,您将实现 JsonConverter&lt;ResponseBase&gt;;为了让生活更轻松,它可以先将responseData 内容反序列化为JObject。这样做,您将能够内省元素的属性,这反过来(希望)允许您提出一种启发式方法来确定元素的实际类型。

一旦知道类型,就可以将JObject 转换为具体实例。这是一个例子:

public class ResponseDataConverter : JsonConverter<ResponseBase>
{
    /// <inheritdoc />
    public override bool CanWrite => false;

    /// <inheritdoc />
    public override ResponseBase ReadJson(JsonReader reader, Type objectType, ResponseBase existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        var jObject = serializer.Deserialize<JObject>(reader);

        // Now, decide tye type by matching patterns.
        if (jObject.TryGetValue("mail", out var mailToken))
        {
            return jObject.ToObject<SubModuleFIMRequest>();
        }

        // TODO: Add more types as needed

        // If nothing matches, you may choose to throw an exception,
        // return a catchall type (e.g. wrapping the JObject), or just
        // return a default value as a last resort.
        throw new JsonSerializationException();
    }

    /// <inheritdoc />
    public override void WriteJson(JsonWriter writer, ResponseBase value, JsonSerializer serializer) => throw new NotImplementedException();
}

请注意,序列化程序不需要编写,因此我们只需输入WriteJson

剩下的就是用指向转换器类型的JsonConverter 属性来注释SubModuleDatafcProperty 属性:

public class SubModuleData
{
    [JsonProperty("fcRequest")]
    [JsonConverter(typeof(ResponseDataConverter))]
    public ResponseBase FcRequest { get; set; }
}

我希望这能让你开始。正如其他 cmets 和答案中提到的那样:如果您可以影响 API 返回 JSON,请尝试更改它。

【讨论】:

  • 这真的很有帮助。 API 来自我无法控制的工作的特许连接工具,我的第一个想法是它没有以最好的格式返回。但这似乎是一个很好的解决方案
【解决方案2】:

如果您可以控制返回的 Json,我强烈建议您在其自己的属性下返回每个对象,并将不需要的任何内容设置为 null。

如果这不是一个选项,另一件可行的方法是将“fcRequest”反序列化为动态,然后尝试转换为它可能是的类型或基于另一个属性名称进行转换。那不是很干净。

另一个 Json 库 (Jil) 中的另一个有趣的方法是联合。 "Jil has limited support for "unions" (fields on JSON objects that may contain one of several types), provided that they can be distiguished by their first character."

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-05
    • 1970-01-01
    • 1970-01-01
    • 2021-08-18
    • 2017-06-24
    • 2023-03-05
    相关资源
    最近更新 更多