将REST资源反序列化为通用对象

时间:2017-12-21 10:54:38

标签: c# rest deserialization restsharp

我正在编写一个程序,用于在学生记录系统和财务系统之间集成数据。 SRS是基于云的解决方案,而财​​务系统是在本地。使用RESTful API从SRS中提取数据。然后,我计划将返回的资源写入临时数据库,以便与财务系统集成。经过一番研究后,我决定使用RestSharp作为我解决方案的基础。

按照Recommended Usage指南,我能够获得一个成功发出请求并将响应数据反序列化到我的类中的解决方案。然而,我遇到了一个小的减速带,需要一些帮助才能再次开始。

SRS Rest API包含资源PostalAddressOccupancy。根据SRS API文档,它具有以下结构:

enter image description here

我的问题在于占用者的财产。如混合类型所示,这可以是许多其他资源类型中的任何一种(即学生,员工,卫报等)。

我可以使用泛型在我的数据模型中轻松表示这一点。例如

public class PostalAddressOccupancy<T>
{
    public DateTime EffectiveDate { get; set; }
    public DateTime EndDate { get; set; }
    public string EntityType { get; set; }
    public string Href { get; set; }
    public string Id { get; set; }
    public bool IsCorrespondenceAddress { get; set; }
    public T Occupant { get; set; }
    public PostalAddress PostalAddress { get; set; }
    public string PostalAddressType { get; set; }

    public static PostalAddressOccupancy<T> GetPostalAddressOccupancy(string id)
    {
        var request = new RestRequest
        {
            Resource = "PostalAddressOccupancy/{Id}",
            RootElement = "postalAddressOccupancy"
        };

        request.AddParameter("Id", id, ParameterType.UrlSegment);

        RestApiClient restApiClient = new RestApiClient("SRS API");
        return restApiClient.Execute<PostalAddressOccupancy<T>>(request);
    }

    public static List<PostalAddressOccupancy<T>> GetPostalAddressOccupancies()
    {
        List<PostalAddressOccupancy<T>> list = new List<PostalAddressOccupancy<T>>();

        var request = new RestRequest
        {
            Resource = "PostalAddressOccupancy",
            RootElement = "postalAddressOccupancies"
        };

        RestApiClient restApiClient = new RestApiClient("SRS API");

        foreach (var pao in restApiClient.Execute<List<PostalAddressOccupancy<T>>>(request))
        {
            list.Add(GetPostalAddressOccupancy(pao.Href.Split('/').Last()));
        }

        return list;
    }
}

我的问题是如何让RestSharp正确理解响应数据中返回哪种类型的占用者。使用上面的GetPostalAddressOccupancies方法,我得到一个对象列表,其中所有内容都与占用者正确反序列化。它作为基础对象类型返回,其属性包含为键/值对。

我是否需要使用某些标签来装饰我的类或泛型属性以帮助提供RestSharp反序列化器的提示?

我还注意到资源包括基本属性(例如entityType)。因此,通过这种方式可以识别出乘员类型,但我不确定这可能有什么帮助。

1 个答案:

答案 0 :(得分:1)

在幕后,RestSharp使用SimpleJson进行JSON序列化。与Json.NET不同,此序列化程序不具有对多态属性反序列化,自定义转换器或代理替换的内置支持。

因此,您需要做的是将您的响应解析为中间对象 - 特别是JsonObject - 然后根据存在的属性识别占用者的类型,然后反序列化为最终类型。

首先,为PostalAddressOccupancy<T>提取非通用基类会更容易:

public abstract class PostalAddressOccupancy
{
    public DateTime EffectiveDate { get; set; }
    public DateTime EndDate { get; set; }
    public string EntityType { get; set; }
    public string Href { get; set; }
    public string Id { get; set; }
    public bool IsCorrespondenceAddress { get; set; }
    public PostalAddress PostalAddress { get; set; }
    public string PostalAddressType { get; set; }

    public abstract object GetOccupant();
}

public class PostalAddressOccupancy<T> : PostalAddressOccupancy
{
    public T Occupant { get; set; }

    public override object GetOccupant()
    {
        return Occupant;
    }
}

public class PostalAddress
{
    // Or whatever.  Type was not included in the question.
    public string Address { get; set; }
}

现在假设你有各种各样的占用者类型,包括:

public class Student
{
    public string Name { get; set; }
    public string StudentId { get; set; }
}

public class Staff
{
    public string Name { get; set; }
    public string StaffId { get; set; }
}

然后,您可以使用以下查看属性名称的方法,在PostalAddressOccupancy<T> T Student Staffusing SimpleJson; using RestSharp; public static class PostalAddressOccupancyExtensions { public static PostalAddressOccupancy DeserializePostalAddressOccupancy(this RestSharp.IRestResponse response) { var addressObj = (JsonObject)SimpleJson.SimpleJson.DeserializeObject(response.Content); var type = PostalAddressOccupancyExtensions.InferPostalAddressOccupancyType(addressObj); return (PostalAddressOccupancy)SimpleJson.SimpleJson.DeserializeObject(response.Content, type); } static Type InferPostalAddressOccupancyType(JsonObject root) { var occupantObj = root["Occupant"]; if (occupantObj is JsonObject) { var occupant = (JsonObject)occupantObj; // Add logic to recognize other cases as required. if (occupant.ContainsKey("StudentId")) { return typeof(PostalAddressOccupancy<Student>); } else if (occupant.ContainsKey("StaffId")) { return typeof(PostalAddressOccupancy<Staff>); } } // Or throw an exception, if you prefer. return typeof(PostalAddressOccupancy<>).MakeGenericType(new[] { occupantObj.GetType() }); } } 的情况下,将object反序列化为PostalAddressOccupancy实际上存在于JSON中并选择适当的最终类型:

<rootelement>
   <parentelement>
      <mytype>123</mytype>
      <myvalue>abc</myvalue>
   </parentelement>
   <parentelement>
      <mytype></mytype>
      <myvalue>xyz</myvalue>
   </parentelement>
   <parentelement>
      <mytype />
      <myvalue>qwe</myvalue>
   </parentelement>
   <parentelement>
      <myvalue>asdf</myvalue>
   </parentelement>
</rootelement>

请注意,使用非泛型基类型意味着返回签名可以是 <parentelement> <mytype></mytype> <myvalue>xyz</myvalue> </parentelement> <parentelement> <mytype /> <myvalue>qwe</myvalue> </parentelement> <parentelement> <myvalue>asdf</myvalue> </parentelement> 以外的其他内容。样本工作IRestResponse

请注意,如果您愿意使用.Net fiddle,则可以使用其他选项。例如。你可以: