【问题标题】:How do I configure the Web API XML serializer to serialize read-only properties for all classes?如何配置 Web API XML 序列化程序以序列化所有类的只读属性?
【发布时间】:2014-04-22 18:42:01
【问题描述】:

我有一个新的 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 响应。

This answer 提供了一个很好的解决方案来强制特定类的只读属性序列化。但是,我想默认序列化所有类的只读属性。有没有办法配置默认的 DataContractSerializer 来执行此操作,或者我是否需要为我使用的每个类显式定义 DataContractSerializer?或者,这是我什至应该尝试的事情(即,是否存在默认禁用这些属性的序列化的最佳实践原因)?

【问题讨论】:

  • @JohnSaunders,该答案提供了一种方法来覆盖单个类的默认序列化行为。我想覆盖所有类的默认序列化行为。这可能不容易完成,也可能不容易完成,但这就是我提出这个问题的原因。
  • 那么我建议你澄清你的问题。除其他外,您发布的代码比说明问题所需的复杂得多。我刚刚编辑了你的标题,这可能会让你朝着这个方向开始。
  • 感谢您的建议。我相信我已将问题编辑得更明确。如果它仍然模棱两可或者我应该进行其他更改,请告诉我。

标签: c# xml serialization asp.net-web-api


【解决方案1】:

我成功地启用了这个功能,至少是部分启用了。它不像我想要的那样简单,但它确实可以完成工作。我扩展了 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 { }
}

我仍然不满意这个解决方案的不优雅,但它运行良好,可以发货。

我认为反序列化具有只读属性的对象应该不是一个很难解决的问题,但我不得不将这个问题留到另一天。

【讨论】:

    猜你喜欢
    • 2012-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-17
    • 2023-01-24
    相关资源
    最近更新 更多