serialize object with XmlDocument Property

2019-09-19 04:44发布

问题:

I have to many classes that contains some field and properties of type XmlDocument. When I put the objects of these classes in session (such as state Server, SQL State Server) it is necessary to serialize them. But if we have a property of type XmlDocument and add [Serialize] Attribute above our class, the following error will be appears .

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.

this error does not appears for the fields have [NonSerialize] attribute. the properties can’t have the attribute [NonSerialize] because it can be used only for class and struct and event and delegate.

回答1:

Internally, according to the 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, as you have noted, is not so marked, thus cannot be immediately serialized with BinaryFormatter.

XmlDocument can, however, be trivially converted from and to a string -- the XML itself that the document represents. Thus if the XmlDocument field were contained in a type implementing ISerializable, then its GetObjectData() could simply store the corresponding XML string inside the serialization stream. Then the corresponding serialization constructor could extract the XML string and reconstitute the XmlDocument.

Since implementing ISerializable on a pre-existing class can be time consuming, the easiest way to accomplish what you want would be to introduce a small serialization wrapper struct for your XML documents:

[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
}

Then, in the classes you want to serialize, replace your XmlDocument fields (and auto-implemented properties) with wrapper struct fields, e.g.:

[Serializable]
public class TestClass
{
    XmlDocumentSerializationWrapper doc;

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

The implicit operators in the struct handle automatic conversion from and to the wrapper.