简体   繁体   中英

XmlSerializer.Deserialize doesn't work with the base class even with XmlInclude

If I Serialize a derived class, I cannot Deserialize the base class even with XmlInclude attributes. It fails with " was not expected." If I use the same serializer instance that I used to serialize the data, it works. If I create a new serializer (like if it was in another process), it fails.

    [Serializable]
    [XmlInclude(typeof(D))]
    public class B
    {
        public string x { get; set; }
    }
    public class D : B
    {
        public string y { get; set; }
    }

... code snippet ....
    D x = new D();
    using (MemoryStream ms = new MemoryStream())
    {
        XmlSerializer serializer = new XmlSerializer(typeof(D));
        serializer.Serialize(ms, x);
        ms.Flush();

        ms.Seek(0, SeekOrigin.Begin);
        string responseBuffer = Encoding.ASCII.GetString(ms.GetBuffer());

        ms.Seek(0, SeekOrigin.Begin);
        serializer = new XmlSerializer(typeof(B));
        B x2 = (B)serializer.Deserialize(ms);
    }

As I noted before, if I used the original serializer (typeof(D)) it works. If I recreate the serializer (typeof(B)). Thanks in advance for your help,

You have to create serializer for base type only when you want to deserialize any/all of the derived classes automatically through XmlInclude attribute. Your code isn't working because you start off with creating XmlSerializer serializer = new XmlSerializer(typeof(D)); which is a serializer for child type, and has no context of base class.

Later when you try to deserialize this child xml by using Serializer of type B, it fails because generated serialized Xml does not have context of base type.

You have to create Serializer of type B so that generated Xml has the attribute xsi:type which instructs XmlSerializer which child type to instantiate during deserialization.

Change your code to this:

using (MemoryStream ms = new MemoryStream())
{
        XmlSerializer serializer = new XmlSerializer(typeof(B));
        serializer.Serialize(ms, x);
        ms.Flush();

        ms.Seek(0, SeekOrigin.Begin);
        string responseBuffer = Encoding.ASCII.GetString(ms.GetBuffer());

        ms.Seek(0, SeekOrigin.Begin);
        serializer = new XmlSerializer(typeof(B));
        B x2 = (B)serializer.Deserialize(ms);
}

Xml from your earlier code would look like:

<D xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" />

Whereas, code posted above will generate following Xml:

<B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:type="D" />

Note : the Node itself is now B, but additional attribute xsi:type="D" tells the XmlSerializer what is the actual type. This works due to your inclusion of XmlInclude attribute in the parent node.

Also, for generating/serializing xml to string MemoryStream type is not needed. Conversion through ASCII GetString is not ideal. You can use StringWriter and StringReader

You can simplify it

D x = new D();
string xml;
using(var sw = new StringWriter())
{
    var serializer = new XmlSerializer(typeof(B));
    serializer.Serialize(sw, x);
    xml = sw.ToString();
}

B x2;
using(var sr = new StringReader(xml)) 
{
    var serializer = new XmlSerializer(typeof(B));
    x2 = serializer.Deserialize(sr) as B;
}
// if you check instance of x2 in debug, it will be of type D

You are going to need to let the XmlSerializer know about all the types that need to be deserialized.

Create the XmlSerializer with the base class type, then add the other types

Example:

var serializer = new XmlSerializer(typeof(B), new Type[] { typeof(D), typeof(C) });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM