How to XML serialize an array of Lists of objects?

前端 未结 3 1745
终归单人心
终归单人心 2021-01-20 02:53

I am trying to save an array of lists of an object in C# with in a xml file. I succeeded to save a array of an object and a list of objects but not an array of lists of an o

相关标签:
3条回答
  • 2021-01-20 03:10

    As a note, you should use a using block for your StreamWriter (see my comments in the code below).

    Based on the comments to your question, this looks like a bug in .NET. There are a couple of workarounds that I can see:

    Option 1) Use List<List<Box> instead of List<Box>[]

    Option 2) Override List<T> (or create your own IList<T> implementation) that implements System.Xml.Serialization.IXmlSerializable. I tested the code below in .NET 3.5 and 4.5.

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Xml;
    using System.Xml.Schema;
    using System.Xml.Serialization;
    
    namespace experiment
    {
        public class Box
        {
            public int x;
            public Box(int a)
            {
                x = a;
            }
            public Box()
            {
            }
        }
    
        public class XmlList<T> : List<T>, IXmlSerializable
        {
            public XmlSchema GetSchema()
            {
                return null;
            }
    
            public void ReadXml(XmlReader reader)
            {
                reader.MoveToContent();
                reader.ReadStartElement();
                if (reader.IsEmptyElement)
                    return;
                var ser = new XmlSerializer(typeof(T));
                var nodeType = reader.MoveToContent();
                if (nodeType == XmlNodeType.None)
                    return;
                while (nodeType != XmlNodeType.EndElement)
                {
                    var item = (T)ser.Deserialize(reader);
                    if (item == null)
                        continue;
                    Add(item);
                    nodeType = reader.MoveToContent();
                }
                reader.ReadEndElement();
            }
    
            public void WriteXml(XmlWriter writer)
            {
                var ser = new XmlSerializer(typeof(T));
                foreach (T item in this)
                {
                    ser.Serialize(writer, item);
                }
            }
        }
    
        public class Program
        {
            static void Main(string[] args)
            {
                var MainArr = new XmlList<Box>[]
                {
                    new XmlList<Box>
                    {
                        new Box(1),
                        new Box(4)
                    }
                };
                var writer = new XmlSerializer(MainArr.GetType());
    
                // Make sure to use a 'using' block to ensure that the stream gets closed, even if there was a serialization error.
                using (var fileWrite = new StreamWriter(@"test.xml"))
                {
                    writer.Serialize(fileWrite, MainArr);
                }
            }
        }
    }
    
    0 讨论(0)
  • 2021-01-20 03:25

    I reproduced your error message and found it is indeed a bug in the XML serializer from Microsoft.

    When using the XmlSerializer(type) constructor, .NET automatically generates an assembly called AssemblyName.XmlSerializer.dll. This assembly contains the actual classes that do the serialization and deserialization of your code.

    When running this in an empty console application, I first got a NullReferenceException like a few others commenting. When I was experimenting with self-generating the assembly using sgen, I did get reasonable code when using a class deriving from List<List<Box>> (you can only pre-compile classes, so you can't pre-compile a serializer for List<Box>[]).

    I finally got your error message when using this class, which is possibly one of your test classes in your project:

    public class X
    {
        public List<Box>[] Boxes { get; set; }
    }
    

    Using sgen:

    "C:\Program Files (x86)\Microsoft SDKs\Windows\v8.1A\bin\NETFX 4.5.1 Tools\sgen" /t:"expirement.X" /a:ConsoleApplication8.exe /force
    

    This gives me the same error as you. Unfortunately, there is no quick fix. Using another type seems to do the trick. Report this issue at Microsoft Connect.


    As a reference, this is the full code of the serialization (using switch /k in sgen). The code seems so broken, I couldn't quick fix it:

    #if _DYNAMIC_XMLSERIALIZER_COMPILATION
    [assembly:System.Security.AllowPartiallyTrustedCallers()]
    [assembly:System.Security.SecurityTransparent()]
    [assembly:System.Security.SecurityRules(System.Security.SecurityRuleSet.Level1)]
    #endif
    [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
    [assembly: System.Xml.Serialization.XmlSerializerVersionAttribute(ParentAssemblyId = @"c80da358-347f-48cf-88a7-0fda1a15c25b,", Version = @"4.0.0.0")]
    namespace Microsoft.Xml.Serialization.GeneratedAssembly
    {
    
        public class XmlSerializationWriterX : System.Xml.Serialization.XmlSerializationWriter
        {
    
            public void Write4_X(object o)
            {
                WriteStartDocument();
                if (o == null)
                {
                    WriteNullTagLiteral(@"X", @"");
                    return;
                }
                TopLevelElement();
                Write3_X(@"X", @"", ((global::expirement.X)o), true, false);
            }
    
            void Write3_X(string n, string ns, global::expirement.X o, bool isNullable, bool needType)
            {
                if ((object)o == null)
                {
                    if (isNullable) WriteNullTagLiteral(n, ns);
                    return;
                }
                if (!needType)
                {
                    System.Type t = o.GetType();
                    if (t == typeof(global::expirement.X))
                    {
                    }
                    else
                    {
                        throw CreateUnknownTypeException(o);
                    }
                }
                WriteStartElement(n, ns, o, false, null);
                if (needType) WriteXsiType(@"X", @"");
                {
                    global::System.Collections.Generic.List<global::expirement.Box>[] a = (global::System.Collections.Generic.List<global::expirement.Box>[])((global::System.Collections.Generic.List<global::expirement.Box>[])o.@Boxes);
                    if (a != null)
                    {
                        WriteStartElement(@"Boxes", @"", null, false);
                        for (int ia = 0; ia < a.Length; ia++)
                        {
                            {
                                global::System.Collections.Generic.List<global::expirement.Box> aa = (global::System.Collections.Generic.List<global::expirement.Box>)((global::System.Collections.Generic.List<global::expirement.Box>)a[ia]);
                                if ((object)(aa) == null)
                                {
                                    WriteNullTagLiteral(@"ArrayOfBox", @"");
                                }
                                else
                                {
                                    WriteStartElement(@"ArrayOfBox", @"", null, false);
                                    for (int iaa = 0; iaa < ((System.Collections.ICollection)aa).Count; iaa++)
                                    {
                                        Write2_Box(@"Box", @"", ((global::expirement.Box)aa[iaa]), true, false);
                                    }
                                    WriteEndElement();
                                }
                            }
                        }
                        WriteEndElement();
                    }
                }
                WriteEndElement(o);
            }
    
            void Write2_Box(string n, string ns, global::expirement.Box o, bool isNullable, bool needType)
            {
                if ((object)o == null)
                {
                    if (isNullable) WriteNullTagLiteral(n, ns);
                    return;
                }
                if (!needType)
                {
                    System.Type t = o.GetType();
                    if (t == typeof(global::expirement.Box))
                    {
                    }
                    else
                    {
                        throw CreateUnknownTypeException(o);
                    }
                }
                WriteStartElement(n, ns, o, false, null);
                if (needType) WriteXsiType(@"Box", @"");
                WriteElementStringRaw(@"x", @"", System.Xml.XmlConvert.ToString((global::System.Int32)((global::System.Int32)o.@x)));
                WriteEndElement(o);
            }
    
            protected override void InitCallbacks()
            {
            }
        }
    
        public class XmlSerializationReaderX : System.Xml.Serialization.XmlSerializationReader
        {
    
            public object Read4_X()
            {
                object o = null;
                Reader.MoveToContent();
                if (Reader.NodeType == System.Xml.XmlNodeType.Element)
                {
                    if (((object)Reader.LocalName == (object)id1_X && (object)Reader.NamespaceURI == (object)id2_Item))
                    {
                        o = Read3_X(true, true);
                    }
                    else
                    {
                        throw CreateUnknownNodeException();
                    }
                }
                else
                {
                    UnknownNode(null, @":X");
                }
                return (object)o;
            }
    
            global::expirement.X Read3_X(bool isNullable, bool checkType) {
                System.Xml.XmlQualifiedName xsiType = checkType ? GetXsiType() : null;
                bool isNull = false;
                if (isNullable) isNull = ReadNull();
                if (checkType) {
                if (xsiType == null || ((object) ((System.Xml.XmlQualifiedName)xsiType).Name == (object)id1_X && (object) ((System.Xml.XmlQualifiedName)xsiType).Namespace == (object)id2_Item)) {
                }
                else
                    throw CreateUnknownTypeException((System.Xml.XmlQualifiedName)xsiType);
                }
                if (isNull) return null;
                global::expirement.X o;
                o = new global::expirement.X();
                global::System.Collections.Generic.List<global::expirement.Box>[] a_0 = null;
                int ca_0 = 0;
                bool[] paramsRead = new bool[1];
                while (Reader.MoveToNextAttribute()) {
                    if (!IsXmlnsAttribute(Reader.Name)) {
                        UnknownNode((object)o);
                    }
                }
                Reader.MoveToElement();
                if (Reader.IsEmptyElement) {
                    Reader.Skip();
                    return o;
                }
                Reader.ReadStartElement();
                Reader.MoveToContent();
                int whileIterations0 = 0;
                int readerCount0 = ReaderCount;
                while (Reader.NodeType != System.Xml.XmlNodeType.EndElement && Reader.NodeType != System.Xml.XmlNodeType.None) {
                    if (Reader.NodeType == System.Xml.XmlNodeType.Element) {
                        if (((object) Reader.LocalName == (object)id3_Boxes && (object) Reader.NamespaceURI == (object)id2_Item)) {
                            if (!ReadNull()) {
                                global::System.Collections.Generic.List<global::expirement.Box>[] a_0_0 = null;
                                int ca_0_0 = 0;
                                if ((Reader.IsEmptyElement)) {
                                    Reader.Skip();
                                }
                                else {
                                    Reader.ReadStartElement();
                                    Reader.MoveToContent();
                                    int whileIterations1 = 0;
                                    int readerCount1 = ReaderCount;
                                    while (Reader.NodeType != System.Xml.XmlNodeType.EndElement && Reader.NodeType != System.Xml.XmlNodeType.None) {
                                        if (Reader.NodeType == System.Xml.XmlNodeType.Element) {
                                            if (((object) Reader.LocalName == (object)id4_ArrayOfBox && (object) Reader.NamespaceURI == (object)id2_Item)) {
                                                if (!ReadNull()) {
                                                    if ((object)(a_0_0 = (global::System.Collections.Generic.List<global::expirement.Box>[])EnsureArrayIndex(a_0_0, ca_0_0, typeof(global::System.Collections.Generic.List<global::expirement.Box>));a_0_0[ca_0_0++]) == null) a_0_0 = (global::System.Collections.Generic.List<global::expirement.Box>[])EnsureArrayIndex(a_0_0, ca_0_0, typeof(global::System.Collections.Generic.List<global::expirement.Box>));a_0_0[ca_0_0++] = new global::System.Collections.Generic.List<global::expirement.Box>();
                                                    global::System.Collections.Generic.List<global::expirement.Box> a_0_0_0 = (global::System.Collections.Generic.List<global::expirement.Box>)a_0_0 = (global::System.Collections.Generic.List<global::expirement.Box>[])EnsureArrayIndex(a_0_0, ca_0_0, typeof(global::System.Collections.Generic.List<global::expirement.Box>));a_0_0[ca_0_0++];
                                                    if ((Reader.IsEmptyElement)) {
                                                        Reader.Skip();
                                                    }
                                                    else {
                                                        Reader.ReadStartElement();
                                                        Reader.MoveToContent();
                                                        int whileIterations2 = 0;
                                                        int readerCount2 = ReaderCount;
                                                        while (Reader.NodeType != System.Xml.XmlNodeType.EndElement && Reader.NodeType != System.Xml.XmlNodeType.None) {
                                                            if (Reader.NodeType == System.Xml.XmlNodeType.Element) {
                                                                if (((object) Reader.LocalName == (object)id5_Box && (object) Reader.NamespaceURI == (object)id2_Item)) {
                                                                    if ((object)(a_0_0_0) == null) Reader.Skip(); else a_0_0_0.Add(Read2_Box(true, true));
                                                                }
                                                                else {
                                                                    UnknownNode(null, @":Box");
                                                                }
                                                            }
                                                            else {
                                                                UnknownNode(null, @":Box");
                                                            }
                                                            Reader.MoveToContent();
                                                            CheckReaderCount(ref whileIterations2, ref readerCount2);
                                                        }
                                                    ReadEndElement();
                                                    }
                                                }
                                                else {
                                                    if ((object)(a_0_0 = (global::System.Collections.Generic.List<global::expirement.Box>[])EnsureArrayIndex(a_0_0, ca_0_0, typeof(global::System.Collections.Generic.List<global::expirement.Box>));a_0_0[ca_0_0++]) == null) a_0_0 = (global::System.Collections.Generic.List<global::expirement.Box>[])EnsureArrayIndex(a_0_0, ca_0_0, typeof(global::System.Collections.Generic.List<global::expirement.Box>));a_0_0[ca_0_0++] = new global::System.Collections.Generic.List<global::expirement.Box>();
                                                    global::System.Collections.Generic.List<global::expirement.Box> a_0_0_0 = (global::System.Collections.Generic.List<global::expirement.Box>)a_0_0 = (global::System.Collections.Generic.List<global::expirement.Box>[])EnsureArrayIndex(a_0_0, ca_0_0, typeof(global::System.Collections.Generic.List<global::expirement.Box>));a_0_0[ca_0_0++];
                                                }
                                            }
                                            else {
                                                UnknownNode(null, @":ArrayOfBox");
                                            }
                                        }
                                        else {
                                            UnknownNode(null, @":ArrayOfBox");
                                        }
                                        Reader.MoveToContent();
                                        CheckReaderCount(ref whileIterations1, ref readerCount1);
                                    }
                                ReadEndElement();
                                }
                                o.@Boxes = (global::System.Collections.Generic.List<global::expirement.Box>[])ShrinkArray(a_0_0, ca_0_0, typeof(global::System.Collections.Generic.List<global::expirement.Box>), false);
                            }
                        }
                        else {
                            UnknownNode((object)o, @":Boxes");
                        }
                    }
                    else {
                        UnknownNode((object)o, @":Boxes");
                    }
                    Reader.MoveToContent();
                    CheckReaderCount(ref whileIterations0, ref readerCount0);
                }
                ReadEndElement();
                return o;
            }
    
            global::expirement.Box Read2_Box(bool isNullable, bool checkType)
            {
                System.Xml.XmlQualifiedName xsiType = checkType ? GetXsiType() : null;
                bool isNull = false;
                if (isNullable) isNull = ReadNull();
                if (checkType)
                {
                    if (xsiType == null || ((object)((System.Xml.XmlQualifiedName)xsiType).Name == (object)id5_Box && (object)((System.Xml.XmlQualifiedName)xsiType).Namespace == (object)id2_Item))
                    {
                    }
                    else
                        throw CreateUnknownTypeException((System.Xml.XmlQualifiedName)xsiType);
                }
                if (isNull) return null;
                global::expirement.Box o;
                o = new global::expirement.Box();
                bool[] paramsRead = new bool[1];
                while (Reader.MoveToNextAttribute())
                {
                    if (!IsXmlnsAttribute(Reader.Name))
                    {
                        UnknownNode((object)o);
                    }
                }
                Reader.MoveToElement();
                if (Reader.IsEmptyElement)
                {
                    Reader.Skip();
                    return o;
                }
                Reader.ReadStartElement();
                Reader.MoveToContent();
                int whileIterations3 = 0;
                int readerCount3 = ReaderCount;
                while (Reader.NodeType != System.Xml.XmlNodeType.EndElement && Reader.NodeType != System.Xml.XmlNodeType.None)
                {
                    if (Reader.NodeType == System.Xml.XmlNodeType.Element)
                    {
                        if (!paramsRead[0] && ((object)Reader.LocalName == (object)id6_x && (object)Reader.NamespaceURI == (object)id2_Item))
                        {
                            {
                                o.@x = System.Xml.XmlConvert.ToInt32(Reader.ReadElementString());
                            }
                            paramsRead[0] = true;
                        }
                        else
                        {
                            UnknownNode((object)o, @":x");
                        }
                    }
                    else
                    {
                        UnknownNode((object)o, @":x");
                    }
                    Reader.MoveToContent();
                    CheckReaderCount(ref whileIterations3, ref readerCount3);
                }
                ReadEndElement();
                return o;
            }
    
            protected override void InitCallbacks()
            {
            }
    
            string id5_Box;
            string id3_Boxes;
            string id1_X;
            string id2_Item;
            string id6_x;
            string id4_ArrayOfBox;
    
            protected override void InitIDs()
            {
                id5_Box = Reader.NameTable.Add(@"Box");
                id3_Boxes = Reader.NameTable.Add(@"Boxes");
                id1_X = Reader.NameTable.Add(@"X");
                id2_Item = Reader.NameTable.Add(@"");
                id6_x = Reader.NameTable.Add(@"x");
                id4_ArrayOfBox = Reader.NameTable.Add(@"ArrayOfBox");
            }
        }
    
        public abstract class XmlSerializer1 : System.Xml.Serialization.XmlSerializer
        {
            protected override System.Xml.Serialization.XmlSerializationReader CreateReader()
            {
                return new XmlSerializationReaderX();
            }
            protected override System.Xml.Serialization.XmlSerializationWriter CreateWriter()
            {
                return new XmlSerializationWriterX();
            }
        }
    
        public sealed class XSerializer : XmlSerializer1
        {
    
            public override System.Boolean CanDeserialize(System.Xml.XmlReader xmlReader)
            {
                return xmlReader.IsStartElement(@"X", @"");
            }
    
            protected override void Serialize(object objectToSerialize, System.Xml.Serialization.XmlSerializationWriter writer)
            {
                ((XmlSerializationWriterX)writer).Write4_X(objectToSerialize);
            }
    
            protected override object Deserialize(System.Xml.Serialization.XmlSerializationReader reader)
            {
                return ((XmlSerializationReaderX)reader).Read4_X();
            }
        }
    
        public class XmlSerializerContract : global::System.Xml.Serialization.XmlSerializerImplementation
        {
            public override global::System.Xml.Serialization.XmlSerializationReader Reader { get { return new XmlSerializationReaderX(); } }
            public override global::System.Xml.Serialization.XmlSerializationWriter Writer { get { return new XmlSerializationWriterX(); } }
            System.Collections.Hashtable readMethods = null;
            public override System.Collections.Hashtable ReadMethods
            {
                get
                {
                    if (readMethods == null)
                    {
                        System.Collections.Hashtable _tmp = new System.Collections.Hashtable();
                        _tmp[@"expirement.X::"] = @"Read4_X";
                        if (readMethods == null) readMethods = _tmp;
                    }
                    return readMethods;
                }
            }
            System.Collections.Hashtable writeMethods = null;
            public override System.Collections.Hashtable WriteMethods
            {
                get
                {
                    if (writeMethods == null)
                    {
                        System.Collections.Hashtable _tmp = new System.Collections.Hashtable();
                        _tmp[@"expirement.X::"] = @"Write4_X";
                        if (writeMethods == null) writeMethods = _tmp;
                    }
                    return writeMethods;
                }
            }
            System.Collections.Hashtable typedSerializers = null;
            public override System.Collections.Hashtable TypedSerializers
            {
                get
                {
                    if (typedSerializers == null)
                    {
                        System.Collections.Hashtable _tmp = new System.Collections.Hashtable();
                        _tmp.Add(@"expirement.X::", new XSerializer());
                        if (typedSerializers == null) typedSerializers = _tmp;
                    }
                    return typedSerializers;
                }
            }
            public override System.Boolean CanSerialize(System.Type type)
            {
                if (type == typeof(global::expirement.X)) return true;
                return false;
            }
            public override System.Xml.Serialization.XmlSerializer GetSerializer(System.Type type)
            {
                if (type == typeof(global::expirement.X)) return new XSerializer();
                return null;
            }
        }
    }
    
    0 讨论(0)
  • 2021-01-20 03:33

    Try converting the list to an array, before attempting to serialize it:

    List<Box>[] MainArr = new List<Box>[1];
    MainArr[0] = new List<Box>();
    Box Box1 = new Box(1);
    MainArr[0].Add(Box1);
    
    var arr = Array.ConvertAll(MainArr, x => x.ToArray());
    
    System.Xml.Serialization.XmlSerializer writer = new System.Xml.Serialization.XmlSerializer(arr.GetType());
    System.IO.StreamWriter fileWrite = new System.IO.StreamWriter(@"C:\Users\Giorgos\Desktop\ConsoleApplication1\ArrListBox.xml");
    writer.Serialize(fileWrite, arr);
    fileWrite.Close();
    

    The above piece of code produces the following xml on my machine:

        <?xml version="1.0" encoding="utf-8"?>
    <ArrayOfArrayOfBox xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <ArrayOfBox>
        <Box>
          <x>1</x>
        </Box>
      </ArrayOfBox>
    </ArrayOfArrayOfBox>
    
    0 讨论(0)
提交回复
热议问题