简体   繁体   English

如何在ASP.NET MVC 5控制器上从JSON反序列化为抽象参数

[英]How do I deserialize from JSON to an abstract parameter on an ASP.NET MVC 5 controller

I have an abstract object, BaseObject (example name), that has 2 country-specific implementations, UsaObject, and CanadaObject, both of which extend BaseObject. 我有一个抽象对象BaseObject(示例名称),该对象具有2个特定于国家/地区的实现,即UsaObject和CanadaObject,这两个对象均扩展了BaseObject。

public abstract class BaseObject...
public class UsaObject : BaseObject...
public class CanadaObject : BaseObject...

In my controller, I have an action 在我的控制器中,我有一个动作

public void UpdateObject(BaseObject object) {
   ...
}

The action only calls methods on BaseObject, and doesn't need to cast. 该操作仅在BaseObject上调用方法,并且不需要强制转换。

I'm using Json.NET to perform serialization, and when I serialize objects, I am using JsonSerializationSettings.TypeNameHandling.Objects to add the type of my class to the objects. 我使用Json.NET执行序列化,当我序列化对象时,我使用JsonSerializationSettings.TypeNameHandling.Objects将类的类型添加到对象中。 This allows me to easily send the data correctly in to the UI and change what is shown based on the country, without having to worry about the exact time until I need to. 这使我可以轻松地将数据正确地发送到UI并根据国家/地区更改显示的内容,而不必担心直到需要的确切时间。

However, I am having issues posting this data back in the UpdateObject method. 但是,我在将此数据发布回UpdateObject方法时遇到问题。 It fails on deserialization with the following error (note that we are using Glimpse to perform performance analysis on the application, though I don't suspect this as the cause at all), which is caught on an IExceptionFilter.OnException call: 它在反序列化时失败,并显示以下错误(请注意,尽管我根本不怀疑这是引起原因的原因,但我们正在使用Glimpse在应用程序上执行性能分析),该错误被IExceptionFilter.OnException调用捕获:

System.MissingMethodException: Cannot create an abstract class.
   at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck)
   at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
   at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
   at System.Activator.CreateInstance(Type type, Boolean nonPublic)
   at System.Activator.CreateInstance(Type type)
   at System.Web.Mvc.DefaultModelBinder.CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
   at System.Web.Mvc.DefaultModelBinder.BindComplexModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
   at System.Web.Mvc.DefaultModelBinder.BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
   at Castle.Proxies.DefaultModelBinderProxy.BindModel_callback(ControllerContext controllerContext, ModelBindingContext bindingContext)
   at Castle.Proxies.Invocations.DefaultModelBinder_BindModel.InvokeMethodOnTarget()
   at Castle.DynamicProxy.AbstractInvocation.Proceed()
   at Glimpse.Core.Extensibility.CastleInvocationToAlternateMethodContextAdapter.Proceed()
   at Glimpse.Core.Extensions.AlternateMethodContextExtensions.TryProceedWithTimer(IAlternateMethodContext context, TimerResult& timerResult)
   at Glimpse.Core.Extensibility.AlternateMethod.NewImplementation(IAlternateMethodContext context)
   at Glimpse.Core.Extensibility.AlternateTypeToCastleInterceptorAdapter.Intercept(IInvocation invocation)
   at Castle.DynamicProxy.AbstractInvocation.Proceed()
   at Castle.Proxies.DefaultModelBinderProxy.BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
   at System.Web.Mvc.ControllerActionInvoker.GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor)
   at System.Web.Mvc.ControllerActionInvoker.GetParameterValues(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass1e.<BeginInvokeAction>b__16(AsyncCallback asyncCallback, Object asyncState)

Is there a way to modify how MVC 5 is handling deserialization, and if so, at which point in the page lifecycle should I accomplish this? 有没有办法修改MVC 5处理反序列化的方式,如果是,我应该在页面生命周期的哪一点完成此操作?

Turns out I made a bad assumption that MVC uses Json.NET for deserialization (shame on me). 原来,我做出了一个错误的假设,即MVC使用Json.NET进行反序列化(对我感到羞耻)。 MVC has its own ModelBinder, which is DefaultModelBinder by default. MVC有自己的ModelBinder,默认情况下为DefaultModelBinder Bear with me on the solution, it was a little rushed and needs testing. 请耐心等待解决方案,这有点仓促,需要测试。 I was able to extend the DefaultModelBinder, 我能够扩展DefaultModelBinder,

public class AbstractModelBinder : DefaultModelBinder
{
    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
    {
        if (modelType.IsAbstract)
        {
            var typeName = bindingContext.ValueProvider.GetValue("$type");
            if (typeName == null)
                throw new Exception("Cannot create abstract model");

            var type = Type.GetType(typeName);
            if (type == null)
                throw new Exception("Cannot create abstract model");

            if (!type.IsSubclassOf(modelType))
                throw new Exception("Incorrect model type specified");

            var model = Activator.CreateInstance(type);

            // this line is very important.
            // It updates the metadata (and type) of the model on the binding context,
            // which allows MVC to properly reflect over the properties
            // and assign their values. Without this,
            // it will keep the type as specified in the parameter list,
            // and only the properties of the base class will be populated
            bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, type);

            return model;
        }

        return base.CreateModel(controllerContext, bindingContext, modelType);
    }
}

and registered it as my default binder on startup. 并在启动时将其注册为我的默认活页夹。

// in Global.asax.cs on application start
ModelBinders.Binders.DefaultBinder = new AbstractModelBinder();

I only change the behavior when the model type is abstract, and default otherwise. 我仅在模型类型为抽象时更改行为,否则为默认值。 If it's abstract, but I can't create an instance of the type, or the type name is unavailable, etc., then for now I'm throwing an exception, but that's a detail that I can figure out later. 如果它是抽象的,但是我不能创建该类型的实例,或者类型名称不可用,等等,那么现在我抛出一个异常,但这是一个细节,我以后可以弄清楚。 This way, I don't need to use any attributes on my Actions' parameters, and the Json.NET type specifiers are being used as best as I can figure out at the moment. 这样,我就不需要在Actions的参数上使用任何属性,而目前我能弄清楚的是,Json.NET类型说明符得到了最好的利用。 It would be nice to just have Json.NET deserialize the model for me, but this is the best solution I've found thus far. 只需让Json.NET为我反序列化模型,将是很好的选择,但这是到目前为止我发现的最佳解决方案。

I'd like to reference posts that helped me find my answer. 我想参考有助于我找到答案的帖子。 Change the default model binder in asp.net MVC , ASP.NET MVC 3: DefaultModelBinder with inheritance/polymorphism 更改asp.net MVCASP.NET MVC 3中 的默认模型绑定程序 :具有继承/多态性的DefaultModelBinder

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

相关问题 如何反序列化ASP.NET MVC控制器中的json数组数据 - How to deserialize json array data in ASP.NET MVC controller 无法将json反序列化为ASP.Net MVC中的抽象类 - Failing to deserialize json into abstract class in ASP.Net MVC 如何在ASP.NET MVC中反序列化JSON - How to deserialize JSON in ASP.NET MVC 如何从ASP.NET MVC 4中的控制器返回JSON对象? - How do I return a JSON object from controller in ASP.NET MVC 4? 如何接受数组作为 ASP.NET MVC 控制器操作参数? - How do I accept an array as an ASP.NET MVC controller action parameter? 在ASP.Net MVC控制器中,如何获得一种从JSON和Form-urlEncoded格式识别数据的方法? - In ASP.Net MVC controller, how do I get a method to recognize data from JSON and Form-urlEncoded formats? 如何将数据从asp.net MVC控制器发布到非MVC asp.net页面? - How do I POST data from an asp.net MVC controller to a non-MVC asp.net page? 如何从ASP.Net MVC控制器发布JSON数据? - How to post JSON data from ASP.Net MVC controller? 如何在ASP.NET MVC中从控制器将多个集合作为参数传递给View? - How could I pass multiple collections to View as parameter from Controller in ASP.NET MVC? 我如何使用 datapost 将参数从 jqgrid 传递到 controller(使用 MVC4 和 asp.net) - how can i pass parameter from jqgrid to controller with datapost (using MVC4 and asp.net )
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM