我在使用.Descendant从XML文件中检索信息时遇到问题

时间:2010-01-01 17:35:12

标签: c# xml

我收到了关于这方面的帮助,但有一个更简单的例子:

<Andromeda>
  <Name>Andromeda</Name>
  <Backstory>The star-born celestial known as Andromeda is a stranger to Newerth. With no memory of her home or her people, she is driven merely by an innate sense that the hellbourne are vile and the mortal inhabitants of Newerth innocent. Her powerful dimensional magic allows her to bring destruction to the daemons or strength to her new-found allies.</Backstory>
  <HeroType>Agility</HeroType>
  <Damage>39-53</Damage>
  <Armor>3.1</Armor>
  <MoveSpeed>295</MoveSpeed>
  <AttackType>Ranged(400)</AttackType>
  <AttackRate>.75</AttackRate>
  <Strength>16</Strength>
  <Agility>27</Agility>
  <Intelligence>15</Intelligence>
  <Icon>Images/Hero/Andromeda.gif</Icon>
  <IconGlow>Images/Hero/gAndromeda.gif</IconGlow>
  <GamePicture>Images/Hero-InGame/Andromeda.png</GamePicture>
  <DotaVersion>Shendelzare Silkwood the Vengeful Spirit</DotaVersion>
</Andromeda>

要检索信息,我只使用此代码:

public List<string> LoadHeroInformation(string NameOfHero)
    {
        List<string> Info = new List<string>();

        Info.Add(HeroInformation.Descendants(NameOfHero).Single().Element("Name").Value);
        Info.Add(HeroInformation.Descendants(NameOfHero).Single().Element("Backstory").Value);
        Info.Add(HeroInformation.Descendants(NameOfHero).Single().Element("HeroType").Value);
        Info.Add(HeroInformation.Descendants(NameOfHero).Single().Element("Damage").Value);
        Info.Add(HeroInformation.Descendants(NameOfHero).Single().Element("Armor").Value);
        Info.Add(HeroInformation.Descendants(NameOfHero).Single().Element("MoveSpeed").Value);
        Info.Add(HeroInformation.Descendants(NameOfHero).Single().Element("AttackType").Value);
        Info.Add(HeroInformation.Descendants(NameOfHero).Single().Element("AttackRate").Value);
        Info.Add(HeroInformation.Descendants(NameOfHero).Single().Element("Strength").Value);
        Info.Add(HeroInformation.Descendants(NameOfHero).Single().Element("Agility").Value);
        Info.Add(HeroInformation.Descendants(NameOfHero).Single().Element("Intelligence").Value);
        Info.Add(HeroInformation.Descendants(NameOfHero).Single().Element("DotaVersion").Value);

        return Info;
    }

现在我有一个更复杂的XML文件,我不知道如何检索它。关心StackOverflow?

这是XML文件:

<Andromeda>
  <SpellOne>
    <Name>Comet</Name>
    <Icon>Images/Spell/Andromeda/Spell1.gif</Icon>
    <Action>Target Unit</Action>
    <Description>Andromeda rips a comet from her dimension to hurl at an enemy, damaging and stunning them.</Description>
    <Ranks>
      <First>            
        <ManaCost>95</ManaCost>
        <Cooldown>10 Seconds</Cooldown>
        <Effects>Deals 100 Magic damage and stuns target for 1.75 seconds.</Effects>
        <ExtraEffects></ExtraEffects>
      </First>
      <Second>
        <ManaCost>110</ManaCost>
        <Cooldown>10</Cooldown>
        <Effects>Deals 175 Magic damage and stuns target for 1.75 seconds.</Effects>
        <ExtraEffects></ExtraEffects>
      </Second>
      <Third>
        <ManaCost>125</ManaCost>
        <Cooldown>10</Cooldown>
        <Effects>Deals 250 Magic damage and stuns target for 1.75 seconds.</Effects>
        <ExtraEffects></ExtraEffects>
      </Third>
      <Fourth>
        <ManaCost>140</ManaCost>
        <Cooldown>10</Cooldown>
        <Effects>Deals 325 Magic damage and stuns target for 1.75 seconds.</Effects>
        <ExtraEffects></ExtraEffects>
      </Fourth>
    </Ranks>
  </SpellOne>

  <SpellTwo>
    <Name>Aurora</Name>
    <Icon>Images/Spell/Andromeda/Spell2.gif</Icon>
    <Action>Target Position</Action>
    <Description>Andromeda shakes the magnetic field of Newerth, causing an Aurora to erupt, damage, and reduce the armor of all enemies in front of her.</Description>
    <Ranks>
      <First>
        <ManaCost>40</ManaCost>
        <Cooldown>15 Seconds</Cooldown>
        <Effects>Deals 25 damage to targets in a line and applies Aurora for 15 seconds.</Effects>
        <ExtraEffects>-5% Base Damage, -2 Armor</ExtraEffects>
      </First>
      <Second>
        <ManaCost>40</ManaCost>
        <Cooldown>15 Seconds</Cooldown>
        <Effects>Deals 50 damage to targets in a line and applies Aurora for 15 seconds.</Effects>
        <ExtraEffects>-10% Base Damage, -3 Armor</ExtraEffects>
      </Second>
      <Third>
        <ManaCost>40</ManaCost>
        <Cooldown>15 Seconds</Cooldown>
        <Effects>Deals 75 damage to targets in a line and applies Aurora for 15 seconds.</Effects>
        <ExtraEffects>-15% Base Damage, -4 Armor</ExtraEffects>
      </Third>
      <Fourth>
        <ManaCost>40</ManaCost>
        <Cooldown>15 Seconds</Cooldown>
        <Effects>Deals 100 damage to targets in a line and applies Aurora for 15 seconds.</Effects>
        <ExtraEffects>-20% Base Damage, -5 Armor</ExtraEffects>
      </Fourth>
    </Ranks>
  </SpellTwo>
</Andromeda>

3 个答案:

答案 0 :(得分:2)

以下是如何使用Linq解析此问题的开始。关键点是定义一些类来存储信息,并根据需要使用各种Linq方法(Select,ToDictionary等)。此外,可以使用int.Parse或用户定义的函数进一步解析某些部分(请参阅parseCooldown)。正则表达式也可以从结构化文本中提取相关信息。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Xml.Linq;

class HeroParseException : ApplicationException
{
    public HeroParseException(string message) : base(message) {}
}

class Spell
{
    public string Name { get; set; }
    public Dictionary<string, SpellAttributes> SpellAttributesByRank { get; set; }
}

class SpellAttributes
{
    public int ManaCost{ get; set; }
    public TimeSpan Cooldown { get; set; }
    public string Effects { get; set; }
}

class Program
{
    static string NameOfHero = "Andromeda";

    static void Main()
    {
        XDocument doc = XDocument.Load("input.xml");
        var spells = doc.Descendants(NameOfHero).Elements().Select(hero => new Spell
        {
            Name = hero.Element("Name").Value,
            SpellAttributesByRank = hero.Element("Ranks").Elements().ToDictionary(
            rank => rank.Name.ToString(),
            rank => new SpellAttributes
            {
                ManaCost = int.Parse(rank.Element("ManaCost").Value),
                Cooldown = parseCooldown(rank.Element("Cooldown").Value),
                Effects = rank.Element("Effects").Value
            })
        });
    }

    private static TimeSpan parseCooldown(string p)
    {
        Match match = Regex.Match(p, @"(\d+)( Seconds)?");
        if (!match.Success)
            throw new HeroParseException("Format for timespan not supported: " + p);
        int number = int.Parse(match.Groups[1].Value);
        return TimeSpan.FromSeconds(number);
    }
}

显然这里有很多缺失,但它应该让你知道如何进步。

答案 1 :(得分:1)

我只是在猜一点,但我认为这就是你想要的。

Action<IEnumerable<XElement>> recurse_elements = null;  
recurse_elements = (elements) =>
{
    foreach (var element in elements)
    {
        Info.Add(element.Value);
        if (element.HasElements)
        {
             recurse_elements(element.Elements());
        }
    }
};

recurse_elements(
    (from hero in HeroInformation.Elements()
     where hero.Element("Name").Value == NameOfHero
     select hero.Elements()).Single()
);

侧面注意:

重构XML是个好主意。目前,XML扩展将非常具有挑战性。

<Heros>
    <Hero Name="Jim" Age="72" PastTime="Slaying">
        <Spells>
            <Spell Name="Fireball" Damage="1000" Aoe="1000" Description="Kills indiscriminately." />
        </Spells>
    </Hero>
</Heros>

答案 2 :(得分:1)

我认为你会发现很多有用的东西。

首先,在使用XML之前设计您的类。在您的第一个示例中,将您检索到的所有数据元素粘贴到List<string>中只是一条痛苦的道路。你如何找到英雄的情报?通过搜索列表,直到找到第11项?该列表中的数据不像XML中那么容易访问 - 我的意思是,在XML中,您至少可以使用XPath找到一些东西。

接下来,如果可以,请构建XML,以便在数据和元数据之间实现明确的分离。使用

<Hero>
   <Name>Andromeda</Name>

而不是

<Andromeda>

...因为a)“Andromeda”是数据(英雄的名字)而不是元数据(表示数据是英雄的标签),以及b)这样,你不必担心你是怎么做的我要创建一个标记为“Corrupted Disciple”或“Wretched Hag”的XML元素。

第三,不要在元数据中放置冗余信息。例如,在命名元素SpellOneSpellTwo等方面没有什么意义,因为XML中元素的顺序隐含地告诉您哪一个是第一个,第二个等等。这使您免于拥有如果你不得不移动它们来重命名。如果你想要明确识别哪个咒语是哪个咒语,请使用元素或属性来做,例如:

最后,实际上已经有一种技术可以将数据从XML文档和对象中提取出来。它被称为XML反序列化。学习它似乎有点令人生畏(有一个非常好的小方法here),因为为了使用它你必须理解XML和反射(或至少属性)以及它对它施加的各种限制。你的班级设计。但是知道它可以让你免于编写大量非常无聊的代码。