c# - 具有多态性的自定义 JSON 反序列化
问题描述
我想要一个通用的提交数据合约,我可以使用它向 API 端点发送大量不同的请求,使用多态性来定义每个特定请求的合约。
我有一个如下所示的对象层次结构
ProductOrderSubmission
-> IOrder Order
-> string OrderId
-> string User
IOrder
-> IProduct[] Items
EmailOrderSubmission : IOrder
-> IProduct[] Items
-> string ClientName
-> string FromEmail
-> IProduct ReferenceItem
PreOrderSubmission : IOrder
-> IProduct[] Items
-> int AccountNumber
-> DateTime FulfillmentDate
IProduct
-> decimal Price
-> int Quantity
ListedProduct : IProduct
-> decimal Price
-> int Quantity
-> int CatalogId
-> int IdType
PromoProduct : IProduct
-> decimal Price
-> int Quantity
-> DateTime StartDate
-> DateTime EndDate
使用具有两种方法的 API,EmailOrder
并且PreOrder
它们的结构如下所示(请注意,我在模型活页夹中使用“路线”来确定这是哪种顶级订单类型,例如,它是电子邮件订单还是预购订单,您可以在模型活页夹代码中看到这一点)
[HttpPost, Route("email"), System.Web.Http.Description.ResponseType(typeof(OrderSubmissionResult))]
public async Task<IHttpActionResult> EmailOrderAsync([ModelBinder(typeof(ProductOrderSubmissionModelBinder))] ProductOrderSubmission orderSubmission)
{
\\ action code here
}
[HttpPost, Route("preorder"), System.Web.Http.Description.ResponseType(typeof(OrderSubmissionResult))]
public async Task<IHttpActionResult> PreOrderAsync([ModelBinder(typeof(ProductOrderSubmissionModelBinder))] ProductOrderSubmission orderSubmission)
{
\\ action code here
}
所以想法是有一个通用的 ProductOrderSubmission 对象,其中包含所有不同类型请求的有效负载。根据运行时的实际类型和数据,将执行不同的代码路径。
我遇到的问题是能够反序列化具有多态性的有效负载。我希望能够在 ProductOrderSubmission 上调用反序列化并对其进行配置,以便反序列化器知道如何确定 IOrder 是 EmailOrderSubmission 还是 PreOrderSubmission 并反序列化为正确的类型,同样对于每个项目,我希望它能够找出反序列化为 ListedProduct 或 PromoProduct。
我知道我需要一个自定义的反序列化器,但我无法弄清楚如何准确地构造它。我将在下面粘贴代码
模型粘合剂
public class ProductOrderSubmissionModelBinder : IModelBinder
{
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
if (bindingContext.ModelType != typeof(ProductOrderSubmission))
return false;
var content = actionContext.Request.Content.ReadAsStringAsync().Result;
var action = actionContext.Request.RequestUri.Segments.Last();
try
{
var submission = JsonConvert.DeserializeObject<ProductOrderSubmission>
(
content,
new EmailOrderSubmissionConverter(action)
);
bindingContext.Model = submission;
return true;
}
catch (ValidationException ex)
{
bindingContext.ModelState.AddModelError(bindingContext.ModelName, ex.Message);
return false;
}
}
}
EmailOrderSubmissionConverter
public class EmailOrderSubmissionConverter : JsonConverter
{
private readonly string _action;
public EmailOrderSubmissionConverter(string action)
{
_action = action;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var emailSubmission =
JsonConvert.DeserializeObject<EmailOrderSubmission>(
reader.ReadAsString(),
new ListedProductConverter(),
new PromoProductConverter()
);
return emailSubmission;
}
public override bool CanConvert(Type objectType)
{
return _action == "email";
}
}
我还没有实现 ListedProductConverter 和 PromoProductConverter,因为我开始觉得我的方法不正确,因为我不确定如何继续