我成功地启用了这个功能,至少是部分启用了。它不像我想要的那样简单,但它确实可以完成工作。我扩展了 XmlMediaTypeFormatter 并覆盖了受保护的方法 GetSerializer(Type type, object value, HttpContent content) 。在 CodePlex 上调整 Web API 源,我能够达到可以为默认序列化程序设置选项的级别。
public class BetterXmlMediaTypeFormatter : XmlMediaTypeFormatter
{
private ConcurrentDictionary<Type, object> serializerCache = new ConcurrentDictionary<Type, object>();
protected override object GetSerializer(Type type, object value, HttpContent content)
{
return this.GetSerializerForType(type);
}
protected override object GetDeserializer(Type type, HttpContent content)
{
return this.GetSerializerForType(type);
}
private static object CreateDefaultSerializer(Type type, bool throwOnError)
{
Exception exception = null;
object serializer = null;
try
{
new XsdDataContractExporter().GetRootElementName(type);
serializer = new DataContractSerializer(type, new DataContractSerializerSettings() { SerializeReadOnlyTypes = true });
}
catch (Exception caught)
{
exception = caught;
}
if (serializer == null && throwOnError)
{
throw new InvalidOperationException("Failed to create the serializer for type " + type.Name, exception);
}
return serializer;
}
private object GetCachedSerializer(Type type, bool throwOnError)
{
object serializer;
if (!this.serializerCache.TryGetValue(type, out serializer))
{
serializer = CreateDefaultSerializer(type, throwOnError);
this.serializerCache.TryAdd(type, serializer);
}
return serializer;
}
private object GetSerializerForType(Type type)
{
Contract.Assert(type != null, "Type cannot be null");
object serializer = this.GetCachedSerializer(type, true);
if (serializer == null)
{
throw new InvalidOperationException();
}
return serializer;
}
然后我重新配置了 Web API 以将此格式化程序用于 XML 请求。
private static void UseBetterXmlFormatter(HttpConfiguration config)
{
config.Formatters.Clear();
config.Formatters.Add(new JsonMediaTypeFormatter());
config.Formatters.Add(new BetterXmlMediaTypeFormatter());
config.Formatters.Add(new FormUrlEncodedMediaTypeFormatter());
}
这很好用,直到我需要反序列化一个具有只读属性的对象。我的控制器方法每次都收到一个空对象。经过繁琐的调试,我发现只读属性导致了这个问题。不情愿地,我只是对每个属性使用了空的 private set 方法,并带有 very 明确的注释。
public bool Prop1
{
get { return this.myValue.HasValue; }
// HACK: Do nothing in the set. It is only present to enable XML serialization.
private set { }
}
我仍然不满意这个解决方案的不优雅,但它运行良好,可以发货。
我认为反序列化具有只读属性的对象应该不是一个很难解决的问题,但我不得不将这个问题留到另一天。