سؤال

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?

هل كانت مفيدة؟

المحلول

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.

نصائح أخرى

Although I would have to see your IXmlSerializable implementation, my bet would be, that your ReadXml implementation has a reversed order of processing ... e.g.: it first looks for element2 instead of element1. If this is not it, please post your IXmlSerializable implementation.

EDIT

As Marc pointed out, you need to add another read. Problem was, that XmlReader by calling reader.Read() processed collection entry tag, but at the end of your method, you didn't process the end tag /collection, hence another reader.Read() call. This basically prevented the deserialization to proceed correctly.

Generally the correct pattern for ReadXml implementation is to start with:

 bool isEmpty = reader.IsEmptyElement;

 reader.ReadStartElement(); //Start reading the element
 if (isEmpty) //Return on empty element
 {
     return;
 }

And finish with:

reader.ReadEndElement();

Here is also your implementation using the pattern:

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

    bool isEmpty = reader.IsEmptyElement;

    reader.ReadStartElement();
    if (isEmpty)
    {
        return;
    }

    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;
    }

    reader.ReadEndElement();
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top