如何在ASP.NET MVC 5应用中更换JSON序列化提供程序?
在ASP.NET MVC 5中更换JSON序列化器为DataContractSerializer
嘿,我之前在ASP.NET MVC 5项目里刚好遇到过一模一样的需求——用WCF的DataContract来精准控制JSON的序列化/反序列化规则,包括属性映射和序列化范围。下面给你一套亲测可行的方案:
第一步:标记你的实体类
首先要确保你的数据模型正确使用DataContract和DataMember属性,这是控制序列化的核心:
using System.Runtime.Serialization; [DataContract] public class WebhookRequest { // 映射到JSON字段"input_file",只有标记DataMember的属性才会被序列化 [DataMember(Name = "input_file")] public string InputFile { get; set; } [DataMember(Name = "event_type")] public string EventType { get; set; } // 这个属性不会被序列化,因为没有DataMember标记 public string InternalNote { get; set; } } [DataContract] public class WebhookResponse { [DataMember(Name = "status")] public string Status { get; set; } = "success"; [DataMember(Name = "processed_id", EmitDefaultValue = false)] public string ProcessedId { get; set; } }
小提示:EmitDefaultValue = false可以让属性为默认值(比如null、0)时不被序列化到JSON里,很实用。
第二步:自定义DataContract版本的JsonResult
ASP.NET MVC 5默认的JsonResult用的是Newtonsoft.Json,我们需要替换成用DataContractJsonSerializer实现的自定义Result:
using System.Runtime.Serialization.Json; using System.Web.Mvc; public class DataContractJsonResult : JsonResult { public override void ExecuteResult(ControllerContext context) { if (context == null) throw new ArgumentNullException(nameof(context)); var response = context.HttpContext.Response; response.ContentType = string.IsNullOrEmpty(ContentType) ? "application/json" : ContentType; if (ContentEncoding != null) response.ContentEncoding = ContentEncoding; if (Data == null) return; // 使用DataContractJsonSerializer序列化数据 var serializer = new DataContractJsonSerializer(Data.GetType()); serializer.WriteObject(response.OutputStream, Data); } }
之后在Controller里返回这个自定义Result就可以了:
[HttpPost] public ActionResult HandleWebhook(WebhookRequest request) { // 这里处理你的Webhook业务逻辑 var response = new WebhookResponse { ProcessedId = "12345" }; // 返回DataContract序列化的JSON响应 return new DataContractJsonResult { Data = response }; }
第三步:替换模型绑定的反序列化器
上面的步骤解决了响应序列化的问题,但Webhook传入的JSON请求体要绑定到你的DataContract类,还需要替换默认的模型绑定逻辑:
- 先创建一个自定义的
ValueProviderFactory,用DataContractJsonSerializer反序列化请求体:
using System.IO; using System.Web.Mvc; using System.Runtime.Serialization.Json; using System.Xml; public class DataContractJsonValueProviderFactory : ValueProviderFactory { public override IValueProvider GetValueProvider(ControllerContext controllerContext) { // 只处理JSON类型的请求 if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase)) return null; var request = controllerContext.HttpContext.Request; request.InputStream.Position = 0; // 重置流位置,避免读取不到内容 using (var streamReader = new StreamReader(request.InputStream)) using (var jsonReader = JsonReaderWriterFactory.CreateJsonReader( streamReader.BaseStream, System.Text.Encoding.UTF8, XmlDictionaryReaderQuotas.Max, null)) { // 获取Action的第一个参数类型(也就是你的WebhookRequest类) var parameterType = controllerContext.Controller.ActionDescriptor.ParameterTypes.First(); var serializer = new DataContractJsonSerializer(parameterType); var model = serializer.ReadObject(jsonReader); // 将反序列化后的模型作为值提供者返回 return new RouteDataValueProvider(controllerContext, new System.Web.Routing.RouteData { Values = { { "", model } } }); } } }
- 在
Global.asax.cs的Application_Start方法里注册这个工厂,替换掉默认的JSON值提供者:
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); // 移除默认的JsonValueProviderFactory var defaultJsonFactory = ValueProviderFactories.Factories.OfType<JsonValueProviderFactory>().FirstOrDefault(); if (defaultJsonFactory != null) ValueProviderFactories.Factories.Remove(defaultJsonFactory); // 添加我们自定义的DataContract版本的工厂 ValueProviderFactories.Factories.Add(new DataContractJsonValueProviderFactory()); }
最后要注意的细节
- 所有需要参与序列化/反序列化的嵌套类,也必须标记
[DataContract]和对应的[DataMember]属性 - 确保你的实体类是公共类,并且属性有公开的
get和set方法 - 如果你的Webhook有特殊的JSON格式需求(比如日期格式),可以通过
DataContractJsonSerializerSettings来调整
内容的提问来源于stack exchange,提问作者Eugene
相关产品推荐
相关产品推荐

