IXml可序列化列表项

本文关键字:列表 序列化 IXml | 更新日期: 2023-09-27 18:37:27

为了好玩,我正在尝试开发一个简单的RPG。我希望我的游戏使用 XML 文件,以便玩家可以轻松自定义游戏。

我得到了一个比赛类和一个种族经理类,其中包含所有种族的静态列表。我开始使用 XmlAttributs (XmlRoot, XmlElement...) 以及将可玩比赛写入 XML 的默认序列化程序。

XmlSerializer xs = new XmlSerializer(managedList.GetType());
using (FileStream fs = new FileStream(filePath, FileMode.Create))
{
    xs.Serialize(fs, managedList);
}

它有效,但在我的种族课上,我有技能列表,可用武器......默认序列化程序生成包含这些类的所有属性的数组。

由于我只需要每个对象的一个实例,因此我开始实现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 方法,但返回包含 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();
        }       
    }
}

我想我也许应该阻止读者。所以我添加了以下代码,但它没有解决我的问题(只有第一场比赛是用这个检索的)

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 StackOverFlowQuestion
{
    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;
        }
    }
}

IXml可序列化列表项

最后,我发现了我的错误。实际上,整个列表只使用一个阅读器。如果在读取对象结束时未处于正确的位置,则读取将停止而不会引发异常。

我用以下方法修改了我的循环:

while(reader.NodeType != XmlNodeType.EndElement || reader.Name != "MyClass")

在该循环结束时,我添加了reader.Read(); 跳过</MyClass>并位于列表中下一个元素的开头(或文件的末尾)。