Search code examples
c#xmlserializerixmlserializable

correct serialize but incorrect Deserialize


consider following codes and classes:

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

namespace ConsoleApplication1
{

    public class Element1
    {
        [XmlAttribute]
        public int Order { get; set; }
        public string name1 { get; set; }
        public ElementCollcetion collection { get; set; }
    }

    public class Element2
    {
        [XmlAttribute]
        public int Order { get; set; }
        public string name2 { get; set; }
    }

    public class Elements
    {
        public Element1 element1 { get; set; }
        public Element2 element2 { get; set; }
    }

    public interface IFoo
    {
        string FooName { get; set; }
    }

    public class Foo1 : IFoo
    {
        public string FooName { get; set; }
        public int deff1 { get; set; }
    }

    public class Foo2 : IFoo
    {
        public string FooName { get; set; }
        public bool deff2 { get; set; }
    }

    public class ElementCollcetion : List<IFoo>, IXmlSerializable
    {
        public System.Xml.Schema.XmlSchema GetSchema()
        {
            return null;
        }

        public void ReadXml(System.Xml.XmlReader reader)
        {
            XmlSerializer serializer = null;
            bool flag;

            reader.Read();
            while (true)
            {
                flag = false;

                if (string.Compare(reader.Name, typeof(Foo1).Name) == 0)
                {
                    serializer = new XmlSerializer(typeof(Foo1));
                    flag = true;
                }
                else if (string.Compare(reader.Name, typeof(Foo2).Name) == 0)
                {
                    serializer = new XmlSerializer(typeof(Foo2));
                    flag = true;
                }

                if (flag)
                    this.Add((IFoo)serializer.Deserialize(reader));
                else
                    break;
            }
        }

        public void WriteXml(System.Xml.XmlWriter writer)
        {
            foreach (IFoo foo in this.AsEnumerable())
            {
                XmlSerializer serializer = new XmlSerializer(foo.GetType());
                serializer.Serialize(writer, foo);
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Elements elements = new Elements()
            {
                element1 = new Element1
                {
                    name1 = "Name1",
                    Order = 1,
                    collection = new ElementCollcetion(){ 
                        new Foo1{deff1=10,FooName="FooName1"},
                        new Foo2{deff2=true,FooName="FooName2"}
                    },
                },

                element2 = new Element2
                {
                    name2 = "Name2",
                    Order = 2
                }
            };

            XmlSerializer serializer = new XmlSerializer(typeof(Elements));
            TextWriter textWriter = new StreamWriter(@"d:\ser.xml");
            serializer.Serialize(textWriter, elements);
            textWriter.Close();

            TextReader textReader = new StreamReader(@"d:\ser.xml");
            Elements element = (Elements)serializer.Deserialize(textReader);
            textReader.Close();
        }
    }
}

when i run it, an xml will generated into ser.xml like so:

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <element1 Order="1">
    <name1>Name1</name1>
    <collection>
       <Foo1>
        <FooName>FooName1</FooName>
        <deff1>10</deff1>
      </Foo1>
      <Foo2>
        <FooName>FooName2</FooName>
        <deff2>true</deff2>
      </Foo2>
    </collection>
  </element1>
  <element2 Order="2">
    <name2>Name2</name2>
  </element2>
</Elements>

but it cannot correctly Deserialize the file unless i reorder the elements in the xml like so:

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <element2 Order="2">
    <name2>Name2</name2>
  </element2>
  <element1 Order="1">
    <name1>Name1</name1>
    <collection>
      <Foo1>
        <FooName>FooName1</FooName>
        <deff1>10</deff1>
      </Foo1>
      <Foo2>
        <FooName>FooName2</FooName>
        <deff2>true</deff2>
      </Foo2>
    </collection>
  </element1>
</Elements>

note that serializer.UnknownAttribute and serializer.UnknownElement will not raise during both execution.
what is the problem? and how can i fix it?

---------------EDIT----------------------
i know that problem is in IXmlSerializable.ReadXml() implementation. but what kind of problem and how should i cure it?


Solution

  • Basically, you aren't progressing the reader to the end of the sub-tree correctly. Adding reader.Read(); to the end of ReadXml fixes it, but is a bit ugly; ReadSubtree() may be safer.

    Frankly, implementing IXmlSerializable correctly and robustly is hard. I always advise against it.