简体   繁体   English

如何动态反序列化 json 对象?

[英]How to Dynamically Deserialize json Object?

I am trying to make my code more simpler and avoid redundant code.我试图让我的代码更简单并避免冗余代码。 I have a function that will accept an object, and a json response from an API call.我有一个接受对象的函数,以及来自 API 调用的 json 响应。 I want to pass in the object, and response, and have it deserialize dynamically.我想传入对象和响应,并让它动态反序列化。 is this possible?这可能吗? i already have classes created for each of the Json files below.我已经为下面的每个 Json 文件创建了类。

private object ParseObject(object obj, string response)
{
     try
     {
         return JsonConvert.DeserializeObject<obj>(response);
     }
     catch (Exception ex)
     {
         return null;
     }
}

Different types of JSON Files不同类型的 JSON 文件

{
  "jobStatus": "4",
  "jobStatusDescription": "Job is complete",
  "resultSet": [
    {
      "equipmentInitial": "BNSF",
      "trainId": "S MEMSCO 1 15"
    }
  ],
  "rowCount": "1"
}

Second One第二个

{
  "intermodalUnits": [
    {
      "attachedIntermodalUnits": [
        {}
      ],
      "carInitial": "INIT",
      "carKindCode": "K18 ",
      "carKindTypeCode": "K",
      "carNumber": "A1B2",
    }
  ]
}

To answer your question, first one needs to create types that match your JSON (please include this in your Minimal Reproduceable Example the next time.要回答您的问题,第一个需要创建与您的 JSON 匹配的类型(请在下次将其包含在您的 Minimal Reproduceable Example 中。

public class Job
{
    public const string TestJson = @"
    {
      ""jobStatus"": ""4"",
      ""jobStatusDescription"": ""Job is complete"",
      ""resultSet"": [
        {
          ""equipmentInitial"": ""BNSF"",
          ""trainId"": ""S MEMSCO 1 15""
        }
      ],
      ""rowCount"": ""1""
    }";

    [JsonProperty("jobStatus")]
    public string JobStatus { get; set; }

    [JsonProperty("jobStatusDescription")]
    public string JobStatusDescription { get; set; }

    [JsonProperty("resultSet")]
    public List<ResultSet> ResultSet { get; set; }

    [JsonProperty("rowCount")]
    public string RowCount { get; set; }

}

public class ResultSet
{
    [JsonProperty("equipmentInitial")]
    public string EquipmentInitial { get; set; }

    [JsonProperty("trainId")]
    public string TrainId { get; set; }
}

and

public class IntermodalUnit
{
    public const string TestJson = @"
    {
      ""intermodalUnits"": [
        {
          ""attachedIntermodalUnits"": [
            {}
          ],
          ""carInitial"": ""INIT"",
          ""carKindCode"": ""K18 "",
          ""carKindTypeCode"": ""K"",
          ""carNumber"": ""A1B2""
        }
      ]
    }";

    [JsonProperty("intermodalUnits")]
    public List<InnerIntermodalUnit> IntermodalUnits { get; set; }
}

public class InnerIntermodalUnit
{
    [JsonProperty("attachedIntermodalUnits")]
    public List<object> AttachedIntermodalUnits { get; set; }

    [JsonProperty("carInitial")]
    public string CarInitial { get; set; }

    [JsonProperty("carKindCode")]
    public string CarKindCode { get; set; }

    [JsonProperty("carKindTypeCode")]
    public string CarKindTypeCode { get; set; }

    [JsonProperty("carNumber")]
    public string CarNumber { get; set; }

}

With that, to answer your question (as asked), I'd do something like this:有了这个,为了回答你的问题(按要求),我会做这样的事情:

private static Lazy<MethodInfo> DeserializeGenericMethod = new Lazy<MethodInfo>(() =>
    typeof(JsonConvert).GetMethods().Where(m => m.Name == "DeserializeObject" && m.ContainsGenericParameters).FirstOrDefault());

private object ParseObject(object obj, string response)
{
    var deserializeSpecificMethod = DeserializeGenericMethod.Value.MakeGenericMethod(obj.GetType());

    try
    {
        return deserializeSpecificMethod.Invoke(null, new object[] { response });
    }
    catch (Exception)
    {
        return null;
    }
}

It uses reflection to invoke JsonConvert.DeserializeObject<T> for the type you specify.它使用反射为您指定的类型调用JsonConvert.DeserializeObject<T>

You'd call the method something like this:你会像这样调用这个方法:

var result = ParseObject(new Job(), Job.TestJson);
var result2 = ParseObject(new IntermodalUnit(), IntermodalUnit.TestJson);

But a better solution would look like:但更好的解决方案如下所示:

private object ParseObject(Type objType, string response)
{
    var deserializeSpecificMethod = DeserializeGenericMethod.Value.MakeGenericMethod(objType);
    // the rest of the method the same.

In that case, you'd call it like this:在这种情况下,您可以这样称呼它:

var result3 = ParseObject(typeof(Job), Job.TestJson);
var result4 = ParseObject(typeof(IntermodalUnit), IntermodalUnit.TestJson);

Note that this way, you don't have to use a (possibly newly created) object that really is never used.请注意,通过这种方式,您不必使用真正从未使用过的(可能是新创建的)对象。

It would also make sense to make ParseObject a generic method:使ParseObject成为通用方法也很有意义:

private object ParseObject<T>(string response) where T : class
{
    var deserializeSpecificMethod = DeserializeGenericMethod.Value.MakeGenericMethod(typeof(T));
    // the rest of the method the same.

You might even keep both the version that takes the Type and the generic version (with one calling the other) to allow your users a choice您甚至可以同时保留采用Type的版本和通用版本(一个调用另一个),以允许您的用户选择

BUT

Neither of these is a good solution.这些都不是一个好的解决方案。 As others have pointed out, all you are doing is swallowing the exception;正如其他人所指出的,您所做的只是吞下异常; your users will be left in the dark when something goes wrong.当出现问题时,您的用户将被蒙在鼓里。

Instead, consider a variation on the reasonably standard TryXxx pattern.相反,请考虑合理标准TryXxx模式的变体。 Something like this:像这样的东西:

private bool TryParseObject<T>(string response, out object obj) where T : class
{
    var deserializeSpecificMethod = DeserializeGenericMethod.Value.MakeGenericMethod(typeof(T));

    try
    {
        obj = deserializeSpecificMethod.Invoke(null, new object[] { response });
        return true;
    }
    catch (Exception ex)
    {
        obj = ex;
        return false;
    }
}

Note that now your users can check for errors.请注意,现在您的用户可以检查错误。 If there is no error, then the out parameter is the newly deserialized object.如果没有错误,那么out参数就是新反序列化的对象。 If there is an error, then the out parameter is the exception that was thrown.如果有错误,那么out参数就是抛出的异常。

Here's what calling this would look like:这是调用它的样子:

var test1 = TryParseObject<Job>(Job.TestJson, out var result7);
var test2 = TryParseObject<IntermodalUnit>(IntermodalUnit.TestJson, out var result8);
var test3 = TryParseObject<IntermodalUnit>("badJson", out var result9);

BUT again...但是又...

None of this really buys you very much.这些都没有真正让你买得太多。

Postscript :后记
After I finished this, I realized that reflection isn't needed on the generic ( <T> ) versions of this code.完成此操作后,我意识到此代码的通用 ( <T> ) 版本不需要反射。 But, I'll let you fix that...但是,我会让你解决这个问题...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM