将部分XML反序列化为C#中的List <t>

时间:2015-12-07 19:12:15

标签: c# xml linq list serialization

我有以下XML数据:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<data-set xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <record>
        <LevelNum>0</LevelNum>
        <LevelName>Level 0</LevelName>
        <MaxBubbles>40</MaxBubbles>
        <MaxVisibleBubbles>30</MaxVisibleBubbles>
        <StartingPointValue>11</StartingPointValue>
        <MaxLevelTime>78</MaxLevelTime>
        <LevelPassScore>3000</LevelPassScore>
        <TapsToPopStandard>1</TapsToPopStandard>
        <InitialVisibleBubbles>9</InitialVisibleBubbles>
        <LevelDescription>Level 80</LevelDescription>
        <SeqLinear>1</SeqLinear>
        <SeqEven>0</SeqEven>
        <SeqOdd>0</SeqOdd>
        <SeqTriangular>0</SeqTriangular>
        <SeqSquare>0</SeqSquare>
        <SeqLazy>0</SeqLazy>
        <SeqFibonacci>0</SeqFibonacci>
        <SeqPrime>0</SeqPrime>
        <SeqDouble>0</SeqDouble>
        <SeqTriple>0</SeqTriple>
        <SeqPi>0</SeqPi>
        <SeqRecaman>0</SeqRecaman>
    </record>
</data-set>

我目前正在阅读以下数据:

    //---------------------------------------------------------------------------------------------------------
    // ReadLevels
    //---------------------------------------------------------------------------------------------------------
    // Reads the contents of the levelInfo.xml file and builds a list of level objects with the data in the 
    // xml file.  Allows for easy changing of level setup and addint new levels
    //---------------------------------------------------------------------------------------------------------
    public List<Level> ReadLevels(XDocument levelInfo)
    {
        levelInfo = XDocument.Load ("./levelinfo.xml");

        List<Level> lvl = (from level in levelInfo.Root.Descendants ("record") // "record" has to match the record level identifier in the xml file
            select new Level {
                LevelNum = int.Parse (level.Element ("LevelNum").Value),
                LevelName = level.Element ("LevelName").Value,
                MaxBubbles = int.Parse (level.Element ("MaxBubbles").Value),
                MaxVisibleBubbles = int.Parse (level.Element ("MaxVisibleBubbles").Value),
                StartingPointValue = int.Parse (level.Element ("StartingPointValue").Value),
                MaxLevelTime = int.Parse (level.Element ("MaxLevelTime").Value),
                LevelPassScore = int.Parse (level.Element ("LevelPassScore").Value),
                TapsToPopStandard = int.Parse (level.Element ("TapsToPopStandard").Value),
                InitialVisibleBubbles = int.Parse (level.Element ("InitialVisibleBubbles").Value),
                LevelDescription = level.Element ("LevelDescription").Value,
                SeqLinear = (bool)level.Element ("SeqLinear"),
                SeqEven = (bool)level.Element ("SeqEven"),
                SeqOdd = (bool)level.Element ("SeqOdd"),
                SeqTriangular = (bool)level.Element ("SeqTriangular"),
                SeqSquare = (bool)level.Element ("SeqSquare"),
                SeqLazy = (bool)level.Element ("SeqLazy"),
                SeqFibonacci = (bool)level.Element ("SeqFibonacci"),
                SeqPrime = (bool)level.Element ("SeqPrime"),
                SeqDouble = (bool)level.Element ("SeqDouble"),
                SeqTriple = (bool)level.Element ("SeqTriple"),
                SeqPi = (bool)level.Element ("SeqPi"),
                SeqRecaman = (bool)level.Element ("SeqRecaman")
            }).ToList ();

        return lvl;
    }

我目前的关卡数据数据结构如下:

public class Level
{
    public int LevelNum { get; set; }
    public string LevelName { get; set; }
    public int MaxBubbles { get; set; }
    public int MaxVisibleBubbles { get; set; }
    public int StartingPointValue { get; set; }
    public int MaxLevelTime { get; set; }
    public int LevelPassScore { get; set; }
    public int TapsToPopStandard { get; set; }
    public int InitialVisibleBubbles { get; set; }
    public string LevelDescription { get; set; }
    public bool SeqLinear { get; set; }
    public bool SeqEven { get; set; }
    public bool SeqOdd { get; set; }
    public bool SeqTriangular { get; set; }
    public bool SeqSquare { get; set; }
    public bool SeqLazy { get; set; }
    public bool SeqFibonacci { get; set; }
    public bool SeqPrime { get; set; }
    public bool SeqDouble { get; set; }
    public bool SeqTriple { get; set; }
    public bool SeqPi { get; set; }
    public bool SeqRecaman { get; set; }

    public Level ()
    {

    }
}

我认为有一种更好的方法来处理布尔标志属性列表。不是将它们全部列出并单独设置每一个,最好将它们存储在列表或字典中。

我的问题是我不知道如何反序列化XML数据来做到这一点。我已经创建了一个级别对象列表,但我不知道如何以正确的方式创建“列表中的列表”。

是否可以使用嵌套的linq语句创建另一个包含我已经创建的列表的列表,或者我应该采用另一种方式吗?

3 个答案:

答案 0 :(得分:1)

在不使用标志列表的情况下执行此操作的一种简单方法(无论是否有一组标志与强类型属性相对应,都取决于您!)是稍微修改XML:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<data-set xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <record>
        <LevelNum>0</LevelNum>
        <LevelName>Level 0</LevelName>
        <MaxBubbles>40</MaxBubbles>
        <MaxVisibleBubbles>30</MaxVisibleBubbles>
        <StartingPointValue>11</StartingPointValue>
        <MaxLevelTime>78</MaxLevelTime>
        <LevelPassScore>3000</LevelPassScore>
        <TapsToPopStandard>1</TapsToPopStandard>
        <InitialVisibleBubbles>9</InitialVisibleBubbles>
        <LevelDescription>Level 80</LevelDescription>
        <LevelSeq>
            <SeqLinear>1</SeqLinear>
            <SeqEven>0</SeqEven>
            <SeqOdd>0</SeqOdd>
            <SeqTriangular>0</SeqTriangular>
            <SeqSquare>0</SeqSquare>
            <SeqLazy>0</SeqLazy>
            <SeqFibonacci>0</SeqFibonacci>
            <SeqPrime>0</SeqPrime>
            <SeqDouble>0</SeqDouble>
            <SeqTriple>0</SeqTriple>
            <SeqPi>0</SeqPi>
            <SeqRecaman>0</SeqRecaman>
        </LevelSeq>
    </record>
</data-set>

然后稍微修改您的类,以镜像新结构并支持使用XmlSerializer

//Avoids missing xmlns errors with XmlSerializer
[Serializable, XmlRoot("record")]
public class Level
{
    public int LevelNum { get; set; }
    public string LevelName { get; set; }
    public int MaxBubbles { get; set; }
    public int MaxVisibleBubbles { get; set; }
    public int StartingPointValue { get; set; }
    public int MaxLevelTime { get; set; }
    public int LevelPassScore { get; set; }
    public int TapsToPopStandard { get; set; }
    public int InitialVisibleBubbles { get; set; }
    public string LevelDescription { get; set; }

    //gives a hint to the serialiser that the element <LevelSeq> should be used.
    [XmlElement(ElementName="LevelSeq")]
    public LevelSeq Seq {get;set;}

    public Level ()
    {
          Seq=new LevelSeq();
    }
}

public class LevelSeq
{
    public bool SeqLinear { get; set; }
    public bool SeqEven { get; set; }
    public bool SeqOdd { get; set; }
    public bool SeqTriangular { get; set; }
    public bool SeqSquare { get; set; }
    public bool SeqLazy { get; set; }
    public bool SeqFibonacci { get; set; }
    public bool SeqPrime { get; set; }
    public bool SeqDouble { get; set; }
    public bool SeqTriple { get; set; }
    public bool SeqPi { get; set; }
    public bool SeqRecaman { get; set; }
}

然后直接使用序列化器:

void Main()
    {
        var levelInfo = XDocument.Load(@"C:\test\data.xml");

        var serialiser = new XmlSerializer(typeof(Level));
        foreach (var level in levelInfo.Root.Descendants("record"))
        {
           Level newLevel = null;
          using (var reader = level.CreateReader())
          {
            try
            {
                newLevel = serialiser.Deserialize(reader) as Level;
            }
            catch(Exception)
            {
                //something went wrong with de-serialisation!
            }
            Console.WriteLine("Level.LevelNum={0}, evel.LevelSeq.SeqLinear=",
                             newLevel.LevelNum,newLevel.Seq.SeqLinear);     
         }
       }
    }

诚然,它不是最高级的,但它简单而且非常快;一个优点是您也可以使用相同的概念轻松地将您的关卡数据写入磁盘。

答案 1 :(得分:0)

我建议您尝试使用XmlSerializer 它可以直接从XML文件序列化/反序列化为对象。

然后,您可以直接从XML-doc移动到对象,而不是解析每个数据变量:

using (Stream stream = File.OpenRead(path))
        {
            XmlSerializer serializer = new XmlSerializer(typeof(T));
            T deSerializedType = (T)serializer.Deserialize(stream);
            return deSerializedType;
        }

此解决方案适用于包含其他类或其他对象列表的类,只要它们全部公开为包含类的公共属性。

答案 2 :(得分:0)

您可以根据自己的情况调整以下内容。由于我们没有设置值代码,因此我不能假设您知道如何调用设置xml的方法(无论如何)。

public IList<bool> Flags
{
    get
    {
        try
        {
            return self.Element("Flags")
                .Elements()
                .Select(x => (bool)x)
                .ToList();
        }
        catch
        {
            return new bool[] { }.ToList();
        }
    }
    set
    {
        XElement flags = self.GetElement("Flags");
        flags.RemoveAll();
        flags.Add(value.Select(b => new XElement("flag", b)));
    }
}

我使用this public xml library中的GetElement扩展方法,类似于Element()调用,但如果元素不存在则创建元素。

self与您的level变量类似,不过我将其作为Level对象的一部分。所以你的代码可能如下:

public class Level
{
    XElement self;
    public int LevelNum { get; set; }
    public string LevelName { get; set; }
    public int MaxBubbles { get; set; }
    public int MaxVisibleBubbles { get; set; }
    public int StartingPointValue { get; set; }
    public int MaxLevelTime { get; set; }
    public int LevelPassScore { get; set; }
    public int TapsToPopStandard { get; set; }
    public int InitialVisibleBubbles { get; set; }
    public string LevelDescription { get; set; }

    public IList<bool> Flags { the code from above here }

    public Level (XElement e)
    {
        self = e;
    }
}