简体   繁体   中英

XmlSerializer with System.Object objects

I can't seem to find a definitive response to this and it's driving me crazy. The XmlSerializer class seems to work great for anything that is defined and even with nested classes. But when one of the classes contains a System.Object it vomits. Does anyone know an easy way around this?

namespace test
{
    public class SomeList
    {
        public object obj;
    }

    public class Person
    {
        public string first;
        public string last;
    }

    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                Console.WriteLine("Creating object...");
                Person me = new Person();
                me.first = "MyFirst";
                me.last = "MyLast";

                Console.WriteLine("- Serialized: " + serialize_xml<Person>(me));
                Console.WriteLine("");
                Console.WriteLine("Creating object with embedded generic...");
                SomeList test = new SomeList();
                test.obj = me;
                Console.WriteLine("- Serialized: " + serialize_xml<SomeList>(test));

                Console.WriteLine("");
                Console.Write("Press ENTER to exit.");
                Console.ReadLine();
                return;
            }
            catch (Exception e)
            {
                print_exception(e);
                Console.WriteLine("");
                Console.Write("Press ENTER to exit.");
                Console.ReadLine();
                return;
            }
        }

        static string serialize_xml<T>(T obj)
        {
            XmlSerializer xmls = new XmlSerializer(typeof(T));
            using (MemoryStream ms = new MemoryStream())
            {
                XmlWriterSettings settings = new XmlWriterSettings();
                XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
                ns.Add("", "");

                settings.Encoding = Encoding.UTF8;
                settings.Indent = false;
                settings.NewLineChars = "";
                settings.NewLineHandling = NewLineHandling.None;
                settings.NewLineOnAttributes = false;
                settings.ConformanceLevel = ConformanceLevel.Document;
                settings.OmitXmlDeclaration = true;

                using (XmlWriter writer = XmlTextWriter.Create(ms, settings))
                {
                    xmls.Serialize(writer, obj, ns);
                }

                string xml = Encoding.UTF8.GetString(ms.ToArray());

                // remove the BOM character at the beginning which screws up decoding
                if (xml.Length > 0 && xml[0] != '<')
                {
                    xml = xml.Substring(1, xml.Length - 1);
                }
                return xml;
            }
        }

        static void print_exception(Exception e)
        {
            Console.WriteLine(" = Exception Type: " + e.GetType().ToString());
            Console.WriteLine(" = Exception Dat " + e.Data);
            Console.WriteLine(" = Inner Exception: " + e.InnerException);
            Console.WriteLine(" = Exception Message: " + e.Message);
            Console.WriteLine(" = Exception Source: " + e.Source);
            Console.WriteLine(" = Exception StackTrace: " + e.StackTrace);
        }
    }
}

Creates the following output on the console:

 = Exception Type: System.InvalidOperationException
 = Exception Dat System.Collections.ListDictionaryInternal
 = Inner Exception: System.InvalidOperationException: The type test.Person was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically.
   at System.Xml.Serialization.XmlSerializationWriter.WriteTypedPrimitive(String name, String ns, Object o, Boolean xsiType)
   at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterSomeList.Write1_Object(String n, String ns, Object o, Boolean isNullable, Boolean needType)
   at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterSomeList.Write2_SomeList(String n, String ns,SomeList o, Boolean isNullable, Boolean needType)
   at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterSomeList.Write3_SomeList(Object o)
 = Exception Message: There was an error generating the XML document.
 = Exception Source: System.Xml
 = Exception StackTrace:    at System.Xml.Serialization.XmlSerializer.Serialize(XmlWriter xmlWriter, Object o, XmlSerializerNamespaces namespaces, String encodingStyle, String id)
   at System.Xml.Serialization.XmlSerializer.Serialize(XmlWriter xmlWriter, Object o, XmlSerializerNamespaces namespaces)
   at test.Program.serialize_xml[T](T obj) in C:\Users\jchristn\Documents\Visual Studio 2010\Projects\test\test\Program.cs:line 83
   at test.Program.Main(String[] args) in C:\Users\jchristn\Documents\Visual Studio 2010\Projects\test\test\Program.cs:line 47

Assuming there's a fixed set of objects SomeList.obj can contain, and that set is known at compile time, you can specify them on the container class using the XmlIncludeAttribute :

[XmlInclude(typeof(Person))]
[XmlInclude(typeof(Other))]
public class SomeList
{
    public object obj;
}

public class Person
{
    public string first;
    public string last;
}

public class Other
{
    public int MyProperty { get; set; }
}

if The types are not known at compile time, you can specify an array of types in an overload of the XmlSerializer constructor:

XmlSerializer xmls = new XmlSerializer(typeof(T), new Type[] { <...types...> });

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