如何配置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?或者,这是我甚至应该尝试的事情吗(即,默认情况下禁用这些属性的序列化是否有最佳实践的原因)?

如何配置Web API XML序列化程序来序列化所有类的只读属性

我成功地启用了这个功能,至少部分启用了。它并不像我希望的那样直截了当,但它确实起到了作用。我扩展了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 { }
}

我仍然不满意这个解决方案的不雅,但它足够好,可以发货。

我认为用只读属性反序列化对象应该不是一个很难解决的问题,但我必须把这个问题留待以后解决。