使用 XmlDocument 属性 序列化对象

serialize object with XmlDocument Property

我有很多 class 包含一些字段和类型 XmlDocument 的属性。 当我把这些classes的对象放到session中时(比如stateServer,SQLStateServer)需要序列化。 但是如果我们有一个 XmlDocument 类型的 属性 并在我们的 class 上面添加 [Serialize] 属性,就会出现以下错误。

Unable to serialize the session state. In 'StateServer' and 'SQLServer' mode, ASP.NET will serialize the session state objects, and as a result non-serializable objects or MarshalByRef objects are not permitted. The same restriction applies if similar serialization is done by the custom session state store in 'Custom' mode.

对于具有 [NonSerialize] 属性的字段,不会出现此错误。属性不能具有 [NonSerialize] 属性,因为它只能用于 class 和结构、事件和委托。

在内部,根据docs, the state server uses BinaryFormatter to serialize complex types. BinaryFormatter serializes all public and private fields (not properties!) of a class or struct that is marked as [Serializable]. But XmlDocument,正如你所注意到的,没有那么标记,因此不能立即用BinaryFormatter序列化。

然而,

XmlDocument 可以简单地转换为字符串——文档表示的 XML 本身。因此,如果 XmlDocument 字段包含在实现 ISerializable, then its GetObjectData() 的类型中,则可以简单地将相应的 XML 字符串存储在序列化流中。然后相应的序列化构造函数可以提取 XML 字符串并重构 XmlDocument.

由于在预先存在的 class 上实现 ISerializable 可能很耗时,因此实现您想要的最简单方法是为您的 [=37= 引入一个小型序列化包装器结构] 文件:

[Serializable]
public struct XmlDocumentSerializationWrapper : ISerializable
{
    public static implicit operator XmlDocumentSerializationWrapper(XmlDocument data) { return new XmlDocumentSerializationWrapper(data); }

    public static implicit operator XmlDocument(XmlDocumentSerializationWrapper wrapper) { return wrapper.XmlDocument; }

    private readonly XmlDocument xmlDocument;

    public XmlDocument XmlDocument { get { return xmlDocument; } }

    public XmlDocumentSerializationWrapper(XmlDocument xmlDocument)
    {
        this.xmlDocument = xmlDocument;
    }

    public XmlDocumentSerializationWrapper(SerializationInfo info, StreamingContext context)
    {
        var xml = (string)info.GetValue("XmlDocument", typeof(string));
        if (!string.IsNullOrEmpty(xml))
        {
            xmlDocument = new XmlDocument();
            xmlDocument.LoadXml(xml);
        }
        else
        {
            xmlDocument = null;
        }
    }

    #region ISerializable Members

    void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
    {
        if (XmlDocument != null)
        {
            var xml = XmlDocument.OuterXml;
            info.AddValue("XmlDocument", xml);
        }
        else
        {
            info.AddValue("XmlDocument", (string)null);
        }
    }

    #endregion
}

然后,在要序列化的 classes 中,用包装器结构字段替换 XmlDocument 字段(和自动实现的属性),例如:

[Serializable]
public class TestClass
{
    XmlDocumentSerializationWrapper doc;

    public XmlDocument Document { get { return doc; } set { doc = value; } }
}

结构中的隐式运算符处理与包装器之间的自动转换。