IXmlSerializable 列表项
IXmlSerializable list item
为了好玩,我正在尝试开发一个简单的角色扮演游戏。我希望我的游戏使用 XML 文件,以便玩家轻松自定义游戏。
我得到了一个比赛 class 和一个 raceManager class,其中包含所有比赛的静态列表。
我首先使用 XmlAttributs(XmlRoot,XmlElement...)和默认序列化程序将可玩的比赛写入 Xml。
XmlSerializer xs = new XmlSerializer(managedList.GetType());
using (FileStream fs = new FileStream(filePath, FileMode.Create))
{
xs.Serialize(fs, managedList);
}
它有效,但在我的比赛中 class,我有可用的技能、武器列表...
默认序列化程序生成包含这些 classes.
所有属性的数组
因为我只想要每个对象的单个实例,所以我开始实现 IXmlSerializable 接口,以便在读取时只写入 id 和检索对象。
现在 Xml 文件和我想要的差不多了 :
<?xml version="1.0"?>
<ArrayOfRace xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Race>
<NameMale>Human</NameMale>
...
<ArmorsTypesAllowed>
<ArmorType>Cloth</ArmorType>
<ArmorType>Leather</ArmorType>
<ArmorType>Metal</ArmorType>
</ArmorsTypesAllowed>
</Race>
<Race>
<NameMale>Dwarf</NameMale>
...
</Race>
...
但我再也看不懂了:'(
我使用我的管理器方法(之前工作正常):
protected static List<T> ReadFile(string filePath)
{
List<T> ManagedList = new List<T>();
XmlSerializer xs = new XmlSerializer(ManagedList.GetType());
using (FileStream fs = new FileStream(filePath, FileMode.Open))
{
ManagedList = (List<T>)xs.Deserialize(fs);
}
return ManagedList;
}
使用 ReadXml 方法,但 return 一个包含 1 个元素(最后一个)的列表。
看起来整个文件(包含列表)被发送到我的 ReadXml,它应该只读取该列表的一个元素。
public void ReadXml(XmlReader reader)
{
while(!reader.EOF)
{
if(reader.NodeType == XmlNodeType.Element)
{
switch(reader.Name)
{
case "NameMale":
NameMale = reader.ReadElementContentAsString();
break;
case "NameFemale":
NameFemale = reader.ReadElementContentAsString();
break;
...
default:
reader.Read();
break;
}
}
else
{
reader.Read();
}
}
}
我想我也许应该停止 reader。所以我添加了以下代码,但它并没有解决我的问题(只有第一场比赛是用这个检索的)
if(reader.NodeType == XmlNodeType.EndElement && reader.Name == "Race")
{
return;
}
提前谢谢你,抱歉我的英语不好。
编辑:这是一个带有控制台应用程序的最小但完整的示例:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
namespace WhosebugQuestion
{
class Program
{
static void Main(string[] args)
{
if (!File.Exists(Manager.FilePath))
{
Manager.WriteFile(Manager.FilePath, Manager.GenerateMyClass());
}
Manager.Initialize();
foreach (MyClass mc in Manager.ListOfMyClass)
{
Console.WriteLine("A:" + mc.A);
Console.WriteLine("B:" + mc.B);
foreach(AnotherClass ac in mc.MyList)
{
Console.WriteLine("Item ID: " + ac.Id);
}
}
Console.ReadLine();
}
}
public class MyClass : IXmlSerializable
{
public string A { get; set; }
public string B { get; set; }
public List<AnotherClass> MyList { get; set; }
public MyClass() { }
public MyClass(string a, string b, List<AnotherClass> list)
{
this.A = a;
this.B = b;
this.MyList = list;
}
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
while (!reader.EOF)
{
if (reader.NodeType == XmlNodeType.Element)
{
switch (reader.Name)
{
case "A":
A = reader.ReadElementContentAsString();
break;
case "B":
B = reader.ReadElementContentAsString();
break;
case "MyList":
MyList = new List<AnotherClass>();
reader.Read();
break;
case "ListItem":
string s = reader.ReadElementContentAsString();
MyList.Add(Manager.ListOfAnotherClass.Single(x => x.Id == s));
break;
default:
reader.Read();
break;
}
}
else
{
reader.Read();
}
}
}
public void WriteXml(XmlWriter writer)
{
writer.WriteElementString("A", A);
writer.WriteElementString("B", B);
writer.WriteComment("Only Id must be serialize !");
writer.WriteStartElement("MyList");
if (MyList != null)
{
foreach (AnotherClass ac in MyList)
{
writer.WriteElementString("ListItem", ac.Id);
}
}
writer.WriteEndElement();
}
}
public class AnotherClass
{
public string Id { get; set; }
public string someProperty { get; set; }
public AnotherClass(string id, string prop)
{
this.Id = id;
this.someProperty = prop;
}
}
class Manager
{
public static List<MyClass> ListOfMyClass { get; set; }
public static string FilePath = "MyXmlFile.xml";
//This list should be from another XML file.
private static List<AnotherClass> _listofAnotherClass;
public static List<AnotherClass> ListOfAnotherClass
{
get
{
if (_listofAnotherClass == null)
{
_listofAnotherClass = GenerateAnotherClass();
}
return _listofAnotherClass;
}
}
public static void Initialize()
{
ListOfMyClass = ReadFile(Manager.FilePath);
}
public static List<MyClass> ReadFile(string filePath)
{
List<MyClass> ManagedList = new List<MyClass>();
XmlSerializer xs = new XmlSerializer(ManagedList.GetType());
using (FileStream fs = new FileStream(filePath, FileMode.Open))
{
ManagedList = (List<MyClass>)xs.Deserialize(fs);
}
return ManagedList;
}
public static void WriteFile(string filePath, List<MyClass> managedList)
{
XmlSerializer xs = new XmlSerializer(managedList.GetType());
using (FileStream fs = new FileStream(filePath, FileMode.Create))
{
xs.Serialize(fs, managedList);
}
}
public static List<MyClass> GenerateMyClass()
{
List<MyClass> list = new List<MyClass>();
MyClass m;
m = new MyClass("ValueA", "ValueB", new List<AnotherClass>(Manager.ListOfAnotherClass.Where(x => x.Id == "Id1" || x.Id == "Id3")));
list.Add(m);
m = new MyClass("ValA", "ValB", new List<AnotherClass>(Manager.ListOfAnotherClass.Where(x => x.Id == "Id2" || x.Id == "Id3")));
list.Add(m);
return list;
}
public static List<AnotherClass> GenerateAnotherClass()
{
List<AnotherClass> anotherList = new List<AnotherClass>();
anotherList.Add(new AnotherClass("Id1", "Prop1"));
anotherList.Add(new AnotherClass("Id2", "Prop2"));
anotherList.Add(new AnotherClass("Id3", "Prop3"));
return anotherList;
}
}
}
终于,我发现了我的错误。
事实上,整个列表只用了一个reader。如果this在对象读取结束时不在正确位置,则读取停止,不抛异常。
我修改了我的循环:
while(reader.NodeType != XmlNodeType.EndElement || reader.Name != "MyClass")
在该循环的末尾,我添加了 reader.Read();
跳过 </MyClass>
并位于列表中下一个元素的开头(或文件末尾)。
为了好玩,我正在尝试开发一个简单的角色扮演游戏。我希望我的游戏使用 XML 文件,以便玩家轻松自定义游戏。
我得到了一个比赛 class 和一个 raceManager class,其中包含所有比赛的静态列表。 我首先使用 XmlAttributs(XmlRoot,XmlElement...)和默认序列化程序将可玩的比赛写入 Xml。
XmlSerializer xs = new XmlSerializer(managedList.GetType());
using (FileStream fs = new FileStream(filePath, FileMode.Create))
{
xs.Serialize(fs, managedList);
}
它有效,但在我的比赛中 class,我有可用的技能、武器列表... 默认序列化程序生成包含这些 classes.
所有属性的数组因为我只想要每个对象的单个实例,所以我开始实现 IXmlSerializable 接口,以便在读取时只写入 id 和检索对象。
现在 Xml 文件和我想要的差不多了 :
<?xml version="1.0"?>
<ArrayOfRace xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Race>
<NameMale>Human</NameMale>
...
<ArmorsTypesAllowed>
<ArmorType>Cloth</ArmorType>
<ArmorType>Leather</ArmorType>
<ArmorType>Metal</ArmorType>
</ArmorsTypesAllowed>
</Race>
<Race>
<NameMale>Dwarf</NameMale>
...
</Race>
...
但我再也看不懂了:'(
我使用我的管理器方法(之前工作正常):
protected static List<T> ReadFile(string filePath)
{
List<T> ManagedList = new List<T>();
XmlSerializer xs = new XmlSerializer(ManagedList.GetType());
using (FileStream fs = new FileStream(filePath, FileMode.Open))
{
ManagedList = (List<T>)xs.Deserialize(fs);
}
return ManagedList;
}
使用 ReadXml 方法,但 return 一个包含 1 个元素(最后一个)的列表。 看起来整个文件(包含列表)被发送到我的 ReadXml,它应该只读取该列表的一个元素。
public void ReadXml(XmlReader reader)
{
while(!reader.EOF)
{
if(reader.NodeType == XmlNodeType.Element)
{
switch(reader.Name)
{
case "NameMale":
NameMale = reader.ReadElementContentAsString();
break;
case "NameFemale":
NameFemale = reader.ReadElementContentAsString();
break;
...
default:
reader.Read();
break;
}
}
else
{
reader.Read();
}
}
}
我想我也许应该停止 reader。所以我添加了以下代码,但它并没有解决我的问题(只有第一场比赛是用这个检索的)
if(reader.NodeType == XmlNodeType.EndElement && reader.Name == "Race")
{
return;
}
提前谢谢你,抱歉我的英语不好。
编辑:这是一个带有控制台应用程序的最小但完整的示例:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
namespace WhosebugQuestion
{
class Program
{
static void Main(string[] args)
{
if (!File.Exists(Manager.FilePath))
{
Manager.WriteFile(Manager.FilePath, Manager.GenerateMyClass());
}
Manager.Initialize();
foreach (MyClass mc in Manager.ListOfMyClass)
{
Console.WriteLine("A:" + mc.A);
Console.WriteLine("B:" + mc.B);
foreach(AnotherClass ac in mc.MyList)
{
Console.WriteLine("Item ID: " + ac.Id);
}
}
Console.ReadLine();
}
}
public class MyClass : IXmlSerializable
{
public string A { get; set; }
public string B { get; set; }
public List<AnotherClass> MyList { get; set; }
public MyClass() { }
public MyClass(string a, string b, List<AnotherClass> list)
{
this.A = a;
this.B = b;
this.MyList = list;
}
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
while (!reader.EOF)
{
if (reader.NodeType == XmlNodeType.Element)
{
switch (reader.Name)
{
case "A":
A = reader.ReadElementContentAsString();
break;
case "B":
B = reader.ReadElementContentAsString();
break;
case "MyList":
MyList = new List<AnotherClass>();
reader.Read();
break;
case "ListItem":
string s = reader.ReadElementContentAsString();
MyList.Add(Manager.ListOfAnotherClass.Single(x => x.Id == s));
break;
default:
reader.Read();
break;
}
}
else
{
reader.Read();
}
}
}
public void WriteXml(XmlWriter writer)
{
writer.WriteElementString("A", A);
writer.WriteElementString("B", B);
writer.WriteComment("Only Id must be serialize !");
writer.WriteStartElement("MyList");
if (MyList != null)
{
foreach (AnotherClass ac in MyList)
{
writer.WriteElementString("ListItem", ac.Id);
}
}
writer.WriteEndElement();
}
}
public class AnotherClass
{
public string Id { get; set; }
public string someProperty { get; set; }
public AnotherClass(string id, string prop)
{
this.Id = id;
this.someProperty = prop;
}
}
class Manager
{
public static List<MyClass> ListOfMyClass { get; set; }
public static string FilePath = "MyXmlFile.xml";
//This list should be from another XML file.
private static List<AnotherClass> _listofAnotherClass;
public static List<AnotherClass> ListOfAnotherClass
{
get
{
if (_listofAnotherClass == null)
{
_listofAnotherClass = GenerateAnotherClass();
}
return _listofAnotherClass;
}
}
public static void Initialize()
{
ListOfMyClass = ReadFile(Manager.FilePath);
}
public static List<MyClass> ReadFile(string filePath)
{
List<MyClass> ManagedList = new List<MyClass>();
XmlSerializer xs = new XmlSerializer(ManagedList.GetType());
using (FileStream fs = new FileStream(filePath, FileMode.Open))
{
ManagedList = (List<MyClass>)xs.Deserialize(fs);
}
return ManagedList;
}
public static void WriteFile(string filePath, List<MyClass> managedList)
{
XmlSerializer xs = new XmlSerializer(managedList.GetType());
using (FileStream fs = new FileStream(filePath, FileMode.Create))
{
xs.Serialize(fs, managedList);
}
}
public static List<MyClass> GenerateMyClass()
{
List<MyClass> list = new List<MyClass>();
MyClass m;
m = new MyClass("ValueA", "ValueB", new List<AnotherClass>(Manager.ListOfAnotherClass.Where(x => x.Id == "Id1" || x.Id == "Id3")));
list.Add(m);
m = new MyClass("ValA", "ValB", new List<AnotherClass>(Manager.ListOfAnotherClass.Where(x => x.Id == "Id2" || x.Id == "Id3")));
list.Add(m);
return list;
}
public static List<AnotherClass> GenerateAnotherClass()
{
List<AnotherClass> anotherList = new List<AnotherClass>();
anotherList.Add(new AnotherClass("Id1", "Prop1"));
anotherList.Add(new AnotherClass("Id2", "Prop2"));
anotherList.Add(new AnotherClass("Id3", "Prop3"));
return anotherList;
}
}
}
终于,我发现了我的错误。 事实上,整个列表只用了一个reader。如果this在对象读取结束时不在正确位置,则读取停止,不抛异常。
我修改了我的循环:
while(reader.NodeType != XmlNodeType.EndElement || reader.Name != "MyClass")
在该循环的末尾,我添加了 reader.Read();
跳过 </MyClass>
并位于列表中下一个元素的开头(或文件末尾)。