在事先不知道对象类型的情况下,如何反序列化JSON?

时间:2019-10-31 20:06:08

标签: c# .net json

所以我有一个正在开发的API和前端,当值之一可能是多种不同类型的列表时,我需要一种反序列化JSON的方法。目前,我正在将其反序列化为List<dynamic>,但是在我的上下文中使用它是一个巨大的痛苦。

public class WorkbenchAPI   
{
    public string status { get; set; }
    public HttpStatusCode code { get; set; }
    public string error { get; set; }
    public string guid { get; set; }
    public List<dynamic> results { get; set; }
}

示例JSON

{
    "status": "ok",
    "code": 200,
    "error": null,
    "guid": "1234",
    "results": [
        {
            "SamAccountName": "dizbuster",
            "CN": "Buster, Diz",
            "EmailAddress": "dizbuster@whatever.com",
        }
    ]
}

例如,在上述示例JSON中,结果应反序列化为类型List<ClassA>

{
    "status": "ok",
    "code": 200,
    "error": null,
    "guid": "1234",
    "results": [
        {
            "data": "127.0.0.1",
            "owner": "dizbuster",
            "email": "dizbuster@whatever.com",
        },
        {
            "data": "192.168.0.1",
            "owner": "dizbuster",
            "email": "dizbuster@whatever.com",
        }
    ]
}

与本示例一样,结果应反序列化为List<ClassB>

某些字段名称可能以不同的类型显示,但有些则没有。从功能上讲,它们是两种不同的类型,分别代表两个单独的API调用的输出,因此我希望将其反序列化为特定的对象类型,而不是使用dynamic或包含每个可能字段的整体对象。

当前,我正在使用List<dynamic>并将其序列化回临时JSON字符串,然后再次将其反序列化为List<ClassA>或其他任何形式。这似乎是一种不好的方法,并且使用起来很麻烦。

是否有更好的方法来结构化或处理序列化/反序列化?我对两端都有完全的控制权,因此如果需要,对我来说更改JSON也并不困难。另外,我知道一个特定的API调用将返回看起来像这样的JSON,而另一个API调用将返回看起来像这样的JSON。每种情况下都应产生不同类型的列表

1 个答案:

答案 0 :(得分:1)

由于您事先知道"results"会为每个API调用返回哪种类型的数据,因此可以使WorkbenchAPI类通用:

public abstract class WorkbenchAPI
{
    protected abstract IReadOnlyCollection<dynamic> GetResults();

    public string status { get; set; }
    public HttpStatusCode code { get; set; }
    public string error { get; set; }
    public string guid { get; set; }
    public IReadOnlyCollection<dynamic> results => GetResults();
}

public class WorkbenchAPI<T> : WorkbenchAPI where T : class
{
    protected override IReadOnlyCollection<dynamic> GetResults() => results;
    public new List<T> results { get; set; } = new List<T>();
}

注意:

  • 我将通用属性提取到抽象的基类中,以允许通过预先存在的,不关心特定结果类型的非通用代码来访问它们。

  • 我还向基类添加了IReadOnlyCollection<dynamic> results,以简化与现有代码的集成,例如:

    // Deserialize to the concrete, known type
    var root = JsonConvert.DeserializeObject<WorkbenchAPI<ClassA>>(json);
    
    // Access the results in a type-safe manner:
    var accounts = root.results.Select(r => r.SamAccountName).ToList();
    
    // Upcast to the base class in code where we don't need to know about the specific result type.
    // E.g.: accessing root properties like guid or getting the result count.
    WorkbenchAPI baseRoot = root;
    var count = baseRoot.results.Count;
    var guid = baseRoot.guid;
    

    但是,添加对结果的访问权限作为dynamic对象的非通用只读集合是可选的,如果可以将旧代码重写为通用代码,则可以将其删除。

演示在这里摆弄:https://dotnetfiddle.net/lbTs1z