0

I'm making an application which saves and loads data from an XML file.

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

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.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. Those "Lifeline" and "Hellcode" are like account names, and the user should choose which account data to load.

1
  • 2
    You know if you use the Linq to XML classes you can clean up your code quite a bit. Commented Apr 9, 2011 at 16:05

3 Answers 3

4

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

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

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():

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. 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);
Sign up to request clarification or add additional context in comments.

12 Comments

Note that your "name" variable can not contain an apostrophe.
@Roma: use a lowercase "storage" in SelectNodes() - fixed my answer and this works for me and returns a node.
@BrokenGlass: I've tried, but still nothing. The problem is in this line XmlNode seconds = saveItems.Item(0).SelectSingleNode("Seconds"); The exception appears on this line
gaah I replaced the wrong "storage" in my answer - the line you are having problems with works for me, just make sure you use a lowercase storage - string.Format("storage/Save[@Name = '{0}']", name)
I am struggling with this error for the whole day and nothing helps.
|
1

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

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?

I suspect that you're not getting the right values off of your 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. 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.

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. Since it was wrong, all following accesses to the first result returned null yielding a NullReferenceException. BrokenGlass' answer covers the using the correct 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.

Overall, the code could be simplified much more. I use some LINQ here just to make dealing with invalid names easier. 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. This should also work with the xmlStr provided above.

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

Comments

0
  1. make sure you are giving the right xml file name,
  2. make sure your xml file is not empty
  3. and try this XmlNode seconds = saveItems.Item(0).SelectSingleNode("Seconds").Value;
  4. check here
  5. and check here

1 Comment

Thanks, I've checked all but nothing helped.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.