考虑以下代码和类:
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()
实施中。但是我应该怎样治疗呢?
答案 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();
}