正确序列化但不正确反序列化

时间:2012-07-19 05:54:12

标签: c# xmlserializer ixmlserializable

考虑以下代码和类:

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

当我运行它时,xml将生成到ser.xml中,如下所示:

<?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>

但它无法正确反序列化文件,除非我重新排序xml中的元素,如下所示:

<?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>

请注意serializer.UnknownAttributeserializer.UnknownElement在执行期间不会加注 问题是什么?我该如何解决?

--------------- EDIT ----------------------
我知道问题出在IXmlSerializable.ReadXml()实施中。但是我应该怎样治疗呢?

2 个答案:

答案 0 :(得分:3)

基本上,您没有正确地将阅读器推进到子树的末尾。将reader.Read();添加到ReadXml的末尾会修复它,但有点难看; ReadSubtree()可能更安全。

坦率地说,正确而有力地实施IXmlSerializable 很难。我总是反对它。

答案 1 :(得分:1)

虽然我必须看到你的IXmlSerializable实现,但我的赌注是,你的ReadXml实现具有相反的处理顺序......例如:它首先查找element2而不是element1。如果不是这样,请发布您的IXmlSerializable实现。

修改

正如Marc指出的那样,你需要添加另一个读数。问题是,XmlReader通过调用reader.Read()处理集合条目标记,但在方法结束时,您没有处理结束标记 / collection ,因此另一个reader.Read()调用。这基本上阻止了反序列化的正确进行。

通常,ReadXml实现的正确模式始于:

 bool isEmpty = reader.IsEmptyElement;

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

结束时:

reader.ReadEndElement();

以下是使用模式的实现:

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