2

考虑以下代码和类:

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.UnknownAttribute并且serializer.UnknownElement不会在两次执行期间引发。
问题是什么?我该如何解决?

--------------- EDIT------------
我知道问题正在IXmlSerializable.ReadXml()实施中。但什么样的问题,我应该如何治愈它?

4

2 回答 2

3

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

坦率地说,IXmlSerializable 正确而稳健地实施是很困难的。我总是反对它。

于 2012-07-19T06:34:08.497 回答
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();
}
于 2012-07-19T06:23:23.913 回答