如何配置Web API XML序列化程序来序列化所有类的只读属性
本文关键字:序列化 程序 只读属性 XML 何配置 配置 API Web | 更新日期: 2023-09-27 17:59:40
我有一个新的Web API解决方案,并且我遇到了默认XML序列化程序的问题。我们希望通过一个抽象基类在所有响应对象上使用只读属性,这样做(显然是简化和泛型的):
[DataContract]
public abstract class MyAbstractClass
{
private MyEnumType? myValue = null;
[DataMember]
public bool Prop1
{
get { return this.myValue.HasValue; }
}
}
JSON.net很乐意序列化只读属性,但XML序列化程序不会(即使它们用[DataMember]
修饰)。API需要为XML和JSON响应工作。
这个答案提供了一个很好的解决方案,可以强制特定类的只读属性序列化。但是,我希望默认情况下序列化所有类的只读属性。有没有办法配置默认的DataContractSerializer来实现这一点,或者我需要为我使用的每个类显式定义DataContractSerialize?或者,这是我甚至应该尝试的事情吗(即,默认情况下禁用这些属性的序列化是否有最佳实践的原因)?
我成功地启用了这个功能,至少部分启用了。它并不像我希望的那样直截了当,但它确实起到了作用。我扩展了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());
}
在我需要反序列化一个具有只读属性的对象之前,这项工作做得很好。我的控制器方法每次都收到一个null对象。经过乏味的调试,我发现只读属性导致了这个问题。不情愿的是,我只是在每个属性上使用空的private set
方法,并使用非常的显式注释。
public bool Prop1
{
get { return this.myValue.HasValue; }
// HACK: Do nothing in the set. It is only present to enable XML serialization.
private set { }
}
我仍然不满意这个解决方案的不雅,但它足够好,可以发货。
我认为用只读属性反序列化对象应该不是一个很难解决的问题,但我必须把这个问题留待以后解决。