如何按属性选择XML节点并使用它的子节点数据?

时间:2011-04-09 18:56:35

标签: c# xml

这是我的XML文件

<?xml version="1.0" encoding="utf-8" ?> 
<storage>   
<Save Name ="Lifeline">
 <Seconds>12</Seconds>
 <Minutes>24</Minutes>
 <Hours>9</Hours>
 <Days>25</Days>
 <Months>8</Months>
 <Years>2010</Years>
 <Health>90</Health>
 <Mood>100</Mood>  
</Save> 

<Save Name ="Hellcode">   
 <Seconds>24</Seconds>
 <Minutes>48</Minutes> 
 <Hours>18</Hours>
 <Days>15</Days>
 <Months>4</Months>
 <Years>1995</Years>
 <Health>50</Health>
 <Mood>50</Mood>  
</Save> 

这是一个从XML获取数据并将其加载到应用程序中的代码。

System.IO.StreamReader sr = new System.IO.StreamReader(@"Saves.xml");

System.Xml.XmlTextReader xr = new System.Xml.XmlTextReader(sr);

System.Xml.XmlDocument save = new System.Xml.XmlDocument();

save.Load(xr);


XmlNodeList saveItems = save.SelectNodes("Storage/Save");

XmlNode seconds = saveItems.Item(0).SelectSingleNode("Seconds");
sec = Int32.Parse(seconds.InnerText);

XmlNode minutes = saveItems.Item(0).SelectSingleNode("Minutes");
min = Int32.Parse(minutes.InnerText);

XmlNode hours = saveItems.Item(0).SelectSingleNode("Hours");
hour = Int32.Parse(hours.InnerText);

XmlNode days = saveItems.Item(0).SelectSingleNode("Days");
day = Int32.Parse(days.InnerText);

XmlNode months = saveItems.Item(0).SelectSingleNode("Months");
month = Int32.Parse(months.InnerText);

XmlNode years = saveItems.Item(0).SelectSingleNode("Years");
year = Int32.Parse(years.InnerText);

XmlNode health_ = saveItems.Item(0).SelectSingleNode("Health");
health = Int32.Parse(health_.InnerText);

XmlNode mood_ = saveItems.Item(0).SelectSingleNode("Mood");
mood = Int32.Parse(mood_.InnerText);

问题是此代码从“生命线”节点加载数据。我想使用列表框,并能够选择从哪个节点加载数据。

我尝试从列表框项目内容中获取字符串,然后使用这样的行

XmlNodeList saveItems = save.SelectNodes(string.Format("storage/Save[@Name = '{0}']", name)); 

变量“name”是listboxe项目中的字符串。编译时,此代码提供异常。 有人知道如何通过属性选择并加载来自该XML的nedeed数据吗?

3 个答案:

答案 0 :(得分:1)

如果你可以使用XElement:

XElement xml = XElement.Load(file);
XElement storage = xml.Element("storage");
XElement save = storage.Elements("Save").FirstOrDefault(e => ((string)e.Attribute("Name")) == nameWeWant);
if(null != save) 
{
// do something with it
}

我个人喜欢具有转换为XElement和从XElement转换的属性的类,以隐藏主程序中的细节。 IE说Save类在构造函数中接受一个XElement节点,在全局内部保存它,并且属性读/写它。

示例类:

  public class MyClass
    {
      XElement self;
      public MyClass(XElement self)
      {
         this.self = self;
      }

    public string Name
    {
      get { return (string)(self.Attribute("Name") ?? "some default value/null"); }
      set 
      { 
        XAttribute x = source.Attribute("Name");
        if(null == x)
          source.Add(new XAttribute("Name", value));
        else
          x.ReplaceWith(new XAttribute("Name", value));
      }
    }
   }

然后您可以将搜索更改为:

XElement save = storage.Elements("Save")
    .FirstOrDefault(e => new MyClass(e).Name == NameWeWant);

答案 1 :(得分:0)

select节点返回两个XmlNode对象。

XmlNodeList saveItems = save.SelectNodes("Storage/Save");

稍后在您的代码中,您似乎正在选择第一个并使用saveItems.Item(0)并从中获取值,在这种情况下,它将是具有Name =“LifeLine”的保存节点。因此,如果您要执行saveItems.Item(1)并选择节点及其值,那么您将获得另一组节点。

答案 2 :(得分:0)

由于数据不是那么多,我建议将所有信息加载到保存列表(构造函数),然后从那里绘制用户想要使用的数据......

至于不起作用的东西,我个人使用较低级别的方法来获取我的数据并且它不容易出错。重新设计它以适应您的问题:

int saves = 0;

List<Saves> saveGames = new List<Saves>();

saveGames.Add(new Saves());

while (textReader.Read())
{
   if (textReader.NodeType == XmlNodeType.Element)
      whatsNext = textReader.Name;
   else if (textReader.NodeType == XmlNodeType.Text)
   {
      if (whatsNext == "name")
         saveGames[saves].name = Convert.ToString(textReader.Value);
      //else if statements for the rest of your attributes
      else if (whatsNext == "Save")
      {
         saveGames.Add(new Saves());
         saves++;
      }
   }
   else if (textReader.NodeType == XmlNodeType.EndElement)
      whatsNext = "";
}

基本上将xml文件中的所有内容都放入对象列表中并操纵该列表以填充列表框。而不是保存名称=“...”,将名称属性作为保存中的第一个属性。

代码标签恨我。为什么他们这么容易破解(._。)