【问题标题】:correct serialize but incorrect Deserialize正确的序列化但不正确的反序列化
【发布时间】:2012-07-19 05:54:12
【问题描述】:

考虑以下代码和类:

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

---------------编辑-----------
我知道问题出在IXmlSerializable.ReadXml() 实现中。但是什么样的问题,我应该如何治疗呢?

【问题讨论】:

    标签: c# xmlserializer ixmlserializable


    【解决方案1】:

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

    坦率地说,实现IXmlSerializable 正确而稳健地 很难。我总是建议不要这样做。

    【讨论】:

    • 我必须在IXmlSerializable.ReadXml()方法实现的底部添加一个简单的reader.Read();
    【解决方案2】:

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

    编辑

    正如 Marc 指出的那样,您需要添加另一个阅读。问题是,XmlReader 通过调用 reader.Read() 处理了 collection 条目标记,但是在您的方法结束时,您没有处理结束标记 /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();
    }
    

    【讨论】:

    • @Rok 我已经插入了运行控制台应用程序所需的所有代码。创建一个新的控制台应用程序,然后复制并粘贴有问题的代码并查看结果。
    • 对不起,我完全错过了!
    猜你喜欢
    • 1970-01-01
    • 2012-08-28
    • 1970-01-01
    • 2022-01-02
    • 2011-04-06
    • 1970-01-01
    • 1970-01-01
    • 2015-07-10
    • 1970-01-01
    相关资源
    最近更新 更多