简体   繁体   English

从具有C#属性的XML读取问题

[英]Problem with reading from an XML with attributes in C#

I'm making an application which saves and loads data from an XML file. 我正在制作一个可从XML文件保存和加载数据的应用程序。

Here is my 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> 
</storage>

The thing is that I whant to specify the "save" by loading "name" from a listbox in such a way 问题是我希望通过这样的方式从列表框中加载“名称”来指定“保存”

       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);

       string name = lstSave.SelectedItem.ToString();

       XmlNodeList saveItems = save.SelectNodes("Storage/Save[@Name = name]");

       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);

When I try to run the application the compiler gives NullReferenceException was unhandled "Object reference not set to an instance of an object" on 当我尝试运行应用程序时,编译器给出了未处理的“ NullReferenceException”,即“对象引用未设置为对象的实例”

XmlNode seconds = saveItems.Item(0).SelectSingleNode("Seconds");

So my question is what's wrong and what should I do? 所以我的问题是怎么了,我该怎么办?

Edit: I've even tried this 编辑:我什至尝试过

foreach (XmlNode xn in saveItems)
{
 sec = Int32.Parse(xn["Seconds"].InnerText);
 min = Int32.Parse(xn["Minutes"].InnerText);
 hour = Int32.Parse(xn["Hours"].InnerText);
 day = Int32.Parse(xn["Days"].InnerText);
 month = Int32.Parse(xn["Months"].InnerText);
 year = Int32.Parse(xn["Years"].InnerText);
 health = Int32.Parse(xn["Health"].InnerText);
 mood = Int32.Parse(xn["Mood"].InnerText);
}

but nothing loads at all 但什么都没有加载

=================================================================== ================================================== =================

just to get this quetion easier to understand. 只是为了使这个问题更容易理解。 Here is the code which works and loads all needed data for application, BUT it loads only from "Lifeline" node. 这是工作并加载应用程序所需的所有数据的代码,但仅从“生命线”节点加载。 While compiling, there are no exception and all works pretty fine. 编译时,也不例外,并且一切正常。

System.IO.StreamReader sr = new System.IO.StreamReader(@"Saves.xml"); System.IO.StreamReader sr =新的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);

The problem is that I want to have an ability to choose nodes by "Name" attribute, and I don't know hot to do it using the listbox. 问题是我希望能够通过“名称”(Name)属性选择节点,并且我不知道使用列表框来进行此操作很热。 Those "Lifeline" and "Hellcode" are like account names, and the user should choose which account data to load. 那些“生命线”和“地狱代码”就像帐户名,用户应该选择要加载的帐户数据。

Your XPath query is off - you are currently using name as a literal and not its value, also you need single quotes around it - so replace this 您的XPath查询已关闭-您当前正在使用name作为文字而不是其值,还需要在其周围加上单引号-因此请替换此

XmlNodeList saveItems = save.SelectNodes("Storage/Save[@Name = name]");

with: 有:

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

Edit: also needed lowercase storage in Xpath query - fixed 编辑: Xpath查询中还需要小写字母storage -已修复

This sample code works for me - this should help you find where your other problems are: 此示例代码对我有用-这应该可以帮助您找到其他问题所在:

System.IO.StreamReader sr = new System.IO.StreamReader(@"test.xml");
System.Xml.XmlTextReader xr = new System.Xml.XmlTextReader(sr);
System.Xml.XmlDocument save = new System.Xml.XmlDocument();
save.Load(xr);
string name = "Hellcode";
XmlNodeList saveItems = save.SelectNodes(string.Format("storage/Save[@Name = '{0}']", name));

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

Also I would suggest you replace your SelectNodes() with SelectSingleNode() : 我也建议您用SelectSingleNode()替换SelectNodes() SelectSingleNode()

XmlNode saveItem = save.SelectSingleNode(string.Format("storage/Save[@Name = '{0}']", name));
XmlNode seconds = saveItem.SelectSingleNode("Seconds");

Edit: 编辑:

As suggested an alternative fro XML parsing is Linq to XML - this is pretty much the standard now, don't use anything else if you don't have to. 正如建议的那样,XML解析的另一种替代方法是将Linq转换为XML-现在这已经成为标准,如果不需要,不要使用其他任何东西。 This makes this sample much shorter: 这使该示例更短:

XDocument doc = XDocument.Load("test.xml");
var saveItem = doc.Descendants("Save")
                  .Where(x => (string)x.Attribute("Name") == name)
                  .Single();
int sec = Convert.ToInt32(saveItem.Element("Seconds").Value);

To answer your followup on your other duplicate question: 要回答关于其他重复问题的跟进操作:

I've tried to take string from listbox item content and then use such a line 我试图从列表框项目内容中获取字符串,然后使用这样的行

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

variable "name" is a string from listboxe's item. 变量“名称”是列表框项目中的字符串。 While compiled this code gives exception. 在编译时,此代码给出了异常。 Do somebody knows a way how to select by attribute and load nedeed data from that XML? 有人知道一种如何通过属性选择并从该XML加载所需数据的方法吗?

I suspect that you're not getting the right values off of your ListBox . 我怀疑您没有从ListBox获得正确的值。 It all depends on how you populated it. 这完全取决于您的填充方式。 If you just used the designer to fill your ListBox with your string names, you should be using the SelectedItem property to get the selected name. 如果您只是使用设计器将您的string名称填充到ListBox ,则应该使用SelectedItem属性来获取所选名称。 If on the other hand you populated your ListBox setting the DataSource , set the ValueMember property to the appropriate property name and use SelectedValue to get the value. 另一方面,如果您填充了设置DataSource的ListBox,则将ValueMember属性设置为适当的属性名称,然后使用SelectedValue来获取值。

Though since you never mentioned what the exception was, this is all just an educated guess. 尽管由于您从未提到过例外,但这只是有根据的猜测。


Your problem initially was that you were using the wrong XPath. 最初的问题是您使用了错误的XPath。 Since it was wrong, all following accesses to the first result returned null yielding a NullReferenceException . 由于这是错误的,因此对第一个结果的所有后续访问均返回null从而产生NullReferenceException BrokenGlass' answer covers the using the correct XPath. BrokenGlass的答案涵盖使用正确的XPath。

If the XML you are showing us is indeed what is in the contents of the file and you are getting an appropriate name value, then everything should work here. 如果您向我们显示的XML确实是文件内容中的内容, 并且您获得了适当的name值,那么一切都应在这里工作。

Overall, the code could be simplified much more. 总体而言,可以进一步简化代码。 I use some LINQ here just to make dealing with invalid names easier. 我在这里使用一些LINQ只是为了使处理无效名称更加容易。 You should find that this is working code. 您应该发现这是有效的代码。

var xmlStr = @"<?xml version=""1.0"" encoding=""utf-8"" ?> 
<storage>   
  <Save Name =""Lifeline"">
    <!-- etc... (trimmed off for brevity) -->
  </Save> 
  <Save Name =""Hellcode"">
    <!-- etc... -->
  </Save> 
</storage>
";
var doc = new XmlDocument();
doc.LoadXml(xmlStr);
var name = "Hellcode";
var settings =
    doc.SelectNodes(String.Format("/storage/Save[@Name='{0}']", name))
       .Cast<XmlElement>()
       .Select(e => new
       {
           Seconds = Convert.ToInt32(e["Seconds"].InnerText),
           Minutes = Convert.ToInt32(e["Minutes"].InnerText),
           Hours   = Convert.ToInt32(e["Hours"].InnerText),
           Days    = Convert.ToInt32(e["Days"].InnerText),
           Months  = Convert.ToInt32(e["Months"].InnerText),
           Years   = Convert.ToInt32(e["Years"].InnerText),
           Health  = Convert.ToInt32(e["Health"].InnerText),
           Mood    = Convert.ToInt32(e["Mood"].InnerText),
       })
       .SingleOrDefault();
Trace.WriteLine("settings: " + settings);

I would prefer LINQ to XML here as it is much more cleaner. 我更喜欢LINQ而不是XML,因为它更加干净。 This should also work with the xmlStr provided above. 这也应该与xmlStr提供的xmlStr一起使用。

var doc = XDocument.Parse(xmlStr);
var name = "Hellcode";
var settings =
    doc.Element("storage")
       .Elements("Save")
       .Where(e => e.Attribute("Name").Value == name)
       // or if using XPath, the above could be replaced with:
//  doc.XPathSelectElements(String.Format("/storage/Save[@Name='{0}']", name))
       .Select(e => new
       {
           Seconds = (int)e.Element("Seconds"),
           Minutes = (int)e.Element("Minutes"),
           Hours   = (int)e.Element("Hours"),
           Days    = (int)e.Element("Days"),
           Months  = (int)e.Element("Months"),
           Years   = (int)e.Element("Years"),
           Health  = (int)e.Element("Health"),
           Mood    = (int)e.Element("Mood"),
       })
       .SingleOrDefault();
Trace.WriteLine("settings: " + settings);
  1. make sure you are giving the right xml file name, 确保您提供了正确的xml文件名,
  2. make sure your xml file is not empty 确保您的xml文件不为空
  3. and try this XmlNode seconds = saveItems.Item(0).SelectSingleNode("Seconds").Value; 并尝试使用此XmlNode seconds = saveItems.Item(0).SelectSingleNode("Seconds").Value;
  4. check here 在这里检查
  5. and check here 在这里检查

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM