如何强制ASP.NET Core默认XML序列化器以基类类型序列化对象
解决ASP.NET Core中XmlSerializer以基类类型序列化派生对象的问题
你的问题核心在于ASP.NET Core默认的XmlSerializerOutputFormatter会根据对象的实际运行时类型(Derived)创建序列化器,而不是你声明的返回类型(Base),这就导致了根节点变成了Derived而非期望的base并带上xsi:type标识。
单独使用XmlSerializer时你指定了typeof(Base)作为序列化类型,所以能得到正确结果,但Web API的格式化器默认没有这么做。我们可以通过自定义格式化器来修复这个问题:
步骤1:自定义XmlSerializerOutputFormatter
创建一个继承自XmlSerializerOutputFormatter的子类,重写CreateSerializer方法,让它使用Action返回的声明类型来创建序列化器:
public class TypedXmlSerializerOutputFormatter : XmlSerializerOutputFormatter { public TypedXmlSerializerOutputFormatter(MvcOptions options) : base(options) { } protected override XmlSerializer CreateSerializer(Type type) { // 如果是ActionContext中存在声明的返回类型,优先使用它 if (ActionContext != null && ActionContext.ActionDescriptor is ControllerActionDescriptor descriptor) { var returnType = descriptor.MethodInfo.ReturnType; // 处理IActionResult<T>包装的情况 if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(IActionResult<>)) { returnType = returnType.GetGenericArguments()[0]; } // 处理异步Task<IActionResult<T>>的情况 else if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>)) { var taskReturnType = returnType.GetGenericArguments()[0]; if (taskReturnType.IsGenericType && taskReturnType.GetGenericTypeDefinition() == typeof(IActionResult<>)) { returnType = taskReturnType.GetGenericArguments()[0]; } } return new XmlSerializer(returnType); } // fallback到默认逻辑 return base.CreateSerializer(type); } }
步骤2:替换默认的输出格式化器
在ConfigureServices中,移除默认的XML输出格式化器,替换成我们自定义的版本:
public void ConfigureServices(IServiceCollection services) { services.AddControllers(options => { options.Filters.Add(new ProducesAttribute("application/xml")); options.RespectBrowserAcceptHeader = true; options.InputFormatters.Add(new XmlSerializerInputFormatter(options)); // 移除默认Xml输出格式化器,添加自定义实现 var defaultXmlFormatter = options.OutputFormatters.OfType<XmlSerializerOutputFormatter>().FirstOrDefault(); if (defaultXmlFormatter != null) { options.OutputFormatters.Remove(defaultXmlFormatter); } options.OutputFormatters.Add(new TypedXmlSerializerOutputFormatter(options)); options.AllowEmptyInputInBodyModelBinding = true; }); }
验证效果
现在你的Index接口返回的XML就会变成你期望的格式:
<base xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:type="Derived" propertyA="some text" propertyB="some other text" />
关键说明
- 确保你的基类
Base上已经添加了[XmlInclude(typeof(Derived))],这是XmlSerializer识别派生类型的必要条件,你已经正确配置了这一点。 - 自定义格式化器处理了同步和异步Action的返回类型,覆盖了常见的
IActionResult和Task<IActionResult>场景。
内容的提问来源于stack exchange,提问作者Kostas
相关产品推荐
相关产品推荐

