简体   繁体   中英

I can't serialize a list of objects in C# with XmlSerializer

I have a list of many test implements the interface IDoTest, that I want to store in a file. I also want to read from this file.

It seemed natural to simple use the XmlSerializer to store the objects in my List of IDoTest. But when I do this I get a vague I am sorry I cant do that error in the neighborhood of System.Xml.Serialization.TypeDesc.CheckSupported()

Can the XmlSerializer only do trivial jobs? Or am I missing something? They are talking about custom serialization at MSDN. Here is my simplified code example.

using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;

namespace ConsoleApplication1
{
    public interface IDoTest
    {
        void DoTest();
        void Setup();
    }
    internal class TestDBConnection : IDoTest
    {
        public string DBName;
        public void DoTest()
        {
            Console.WriteLine("DoHardComplicated Test");
        }
        public void Setup()
        {
            Console.WriteLine("SetUpDBTest");
        }
    }
    internal class PingTest : IDoTest
    {
        public string ServerName;
        public void DoTest()
        {
            Console.WriteLine("MaybeDoAPing");
        }
        public void Setup()
        {
            Console.WriteLine("SetupAPingTest");
        }
    }     

    internal class Program
    {
        private static void Main(string[] args)
        {

            TestDBConnection Do1 = new TestDBConnection { DBName = "SQLDB" };
            PingTest Do2 = new PingTest { ServerName = "AccTestServ_5" };
            List<IDoTest> allTest = new List<IDoTest> { Do1, (Do2) };
            // Now I want to serialize my list. 
            // Its here where I get the error at allTest
            XmlSerializer x = new XmlSerializer(allTest.GetType());
            StreamWriter writer = new StreamWriter("mySerializedTestSuite.xml");
            x.Serialize(writer, allTest); 

        }
    }
}

XmlSerializer cannot serialize an interface , and by extension, it cannot serialize a List<> of some interface. It can only serialize concrete object types.

The assumption is that you will probably want to deserialize the objects at some point, and if it only output information pertaining to that interface, it can't guarantee that all the necessary data is present to reconstruct the original objects.

This post shows a potential workaround, if you are able to use an abstract base class and explicitly provide every possible type of object that may appear in the list.

I followed the link that StriplingWarrior gave and found this excellent answer. https://stackoverflow.com/a/15089253/648076 from webturner

I changed his implementation and made a class class ListOfToDo that implemented both List and IXmlSerializable. That worked! Here is my changed code.

using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;

namespace ConsoleApplication1
{
    public interface IDoTest
    {
        void DoTest();
        void Setup();
    }
    public class TestDBConnection : IDoTest
    {
        public string DBName;
        public void DoTest()
        {
            Console.WriteLine("DoHardComplicated Test");
        }
        public void Setup()
        {
            Console.WriteLine("SetUpDBTest");
        }
    }
    public class PingTest : IDoTest
    {
        public string ServerName;
        public void DoTest()
        {
            Console.WriteLine("MaybeDoAPing");
        }
        public void Setup()
        {
            Console.WriteLine("SetupAPingTest");
        }
    }

    public class ListOfToDo : List<IDoTest>, **IXmlSerializable**
    {    
        #region IXmlSerializable
        public XmlSchema GetSchema(){ return null; }

        public void ReadXml(XmlReader reader)

           {
               reader.ReadStartElement("ListOfToDo");
               while (reader.IsStartElement("IDoTest"))
            {
                Type type = Type.GetType(reader.GetAttribute("AssemblyQualifiedName"));
                XmlSerializer serial = new XmlSerializer(type);

                reader.ReadStartElement("IDoTest");
                this.Add((IDoTest)serial.Deserialize(reader));
                reader.ReadEndElement(); //IDoTest
            }
            reader.ReadEndElement(); //IDoTest
        }

        public void WriteXml(XmlWriter writer)
        {
            foreach (IDoTest test in this)
            {
                writer.WriteStartElement("IDoTest");
                writer.WriteAttributeString("AssemblyQualifiedName", test.GetType().AssemblyQualifiedName);
                XmlSerializer xmlSerializer = new XmlSerializer(test.GetType());
                xmlSerializer.Serialize(writer, test);
                writer.WriteEndElement();
            }
        }
         #endregion
    }

    internal class Program
    {
        private static void Main(string[] args)
        {

            TestDBConnection Do1 = new TestDBConnection { DBName = "SQLDB" };
            PingTest Do2 = new PingTest { ServerName = "AccTestServ_5" };
            ListOfToDo allTest = new ListOfToDo { Do1, (Do2) };

            // Now I want to serialize my list. 
            // Its here where I get the error at allTest
            XmlSerializer x = new XmlSerializer(allTest.GetType());
            StreamWriter writer = new StreamWriter("mySerializedTestSuite.xml");
            x.Serialize(writer, allTest); 
            writer.Flush();
            writer.Close();

            //Read it aka deserialize
            {
                var xmlSerializer = new XmlSerializer(typeof(ListOfToDo));
                var xmlReader = XmlReader.Create(new StreamReader("mySerializedTestSuite.xml"));
                ListOfToDo readWhatToTest = (ListOfToDo)xmlSerializer.Deserialize(xmlReader);
                xmlReader.Close();
            }


        }
    }
}

Output will then be:

    <?xml version="1.0" encoding="utf-8"?>
<ListOfToDo>
  <IDoTest AssemblyQualifiedName="ConsoleApplication1.TestDBConnection, ConsoleApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
    <TestDBConnection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <DBName>SQLDB</DBName>
    </TestDBConnection>
  </IDoTest>
  <IDoTest AssemblyQualifiedName="ConsoleApplication1.PingTest, ConsoleApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
    <PingTest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <ServerName>AccTestServ_5</ServerName>
    </PingTest>
  </IDoTest>
</ListOfToDo>

Not sure if this might be the cause of your issue but on these two examples they do use typeof(T) instead of T.GetType()

http://msdn.microsoft.com/en-us/library/71s92ee1.aspx

I can't serialize a list of objects in C# with XmlSerializer

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