Get Data from XML File One Punch Man Training App

Get Data from XML File One Punch Man Training App
Share this to your friends

After creating a saved file we again call the same BeginLoadingSavedData method. This will again check for the now existing XML file. It will end the loop and we can now fetch the data from our saved file. In our SubjectForm.cs put this code outside the while statement.


var loader = new XmlLoader();
      	loader.Load(SavedDataPath);              
      	progress = loader.GetProgress;
	if (progress == null) {
		MessageBox.Show("Problem retrieving progress data.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
		return;
	}

This will create a new instance of our XmlLoader class. We haven’t created the Load and GetProgress methods yet so an error would be shown. Also the progress variable is actually a reference variable for my Progress class. We also haven’t add this class in our project. So let’s continue to the next section and create our Progress class.

Creating the Progress Class in a new Class Library

Again :

  • going to your projects solution explorer
  • right click on your projects name
  • and choose add → New Projects
  • in the Add New Project dialog box, select Class Library
  • and named it whatever you like or as OnePunchManTrainingAppLibrary, the same as what my project has
  • create this Progress class inside this new class library.

One Punch Man Training App Progress Class

The progress class is consist of important properties. This properties returns the value of the subject’s current progress day, hero rank and a list of exercises. The complete code of the Progress class is shown below.


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

namespace OnePunchManTrainingAppLibrary {
    public class Progress {
        public DateTime StartOfTime { get; set; }

        public int Days {
            get {
                int days = (int)(DateTime.Now - StartOfTime).TotalDays;
                return days == 0 ? 1 : days;                
            }
        }

        public string Rank { get; set; }

        public List GetExercises { get; set; }
    }
}

Those properties will be filled by data we fetch from our xml file.

The XmlLoader Class Load Method

We will now fetch all the data from the XML saved file. Starting by initializing a new instance of XDocument class with the XML file path as a parameter. Also we need to put this inside a try and catch block so that we can handle any exception thrown by our code.


public void Load(string filepath) {
      try {
            document = XDocument.Load(filepath);

            if (!IsSavedFileValid(document)) {                
                  MessageBox.Show("Invalid saved file.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                  return;
            }
       } catch(Exception ex) {
             MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);                
       }
}

This is not the complete code. I just want to show a simple try and catch block code. Inside the catch block a MessageBox will be shown each time an exception message is thrown. Also before we start getting our data we need to check if our saved file is a valid XML file.


if (!IsSavedFileValid(document)) {                
      MessageBox.Show("Invalid saved file.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
      return;
}

The code above calls a separate method that checks XML file structures. It returns a boolean value true if all the required condition are met. Else it will stop loading records if it detects inconsistencies in our xml saved file.

The IsSavedFileValid Method

This might not be the best approach. Remember to read our site disclaimer. Anyway let’s add this code to our XmlLoader class and continue creating our One Punch Man Training App.


private bool IsSavedFileValid(XDocument document) {
	bool isValid = false;
        /* 
        * create a list of elements name
        * that will be used for comparing
        * elements name in the xml file
        */
	var elements = new List { mainElement, subElementPushUps, subElementSitUps, subElementSquats, subElementJog };

	try {
		var root = document.Element(rootElement);

		if (root.IsEmpty && !root.Attributes(rootAttributesTime).Any() || !root.Attributes(rootAttributesRank).Any()) {
			return false;
		}

		var xmlElements = document.Root.DescendantNodes().OfType()
                              .Select(x => x.Name).Distinct().ToList();
                
		if(xmlElements != null) {
			foreach (var element in elements) {
				if (!xmlElements.Contains(element.ToString())) {
					isValid = false;
				}

				if (element.ToString().Equals(mainElement)) {
					var Progress = document.Descendants(element).ToList();

					if (Progress.Attributes(mainElementAttributeDay).Any()) {
						isValid = true;
					}
				}
			}
		}
	} catch (Exception ex) {
		MessageBox.Show(string.Format("Invalid saved data. {0}", ex.Message), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
	}
	return isValid;
}

This code is actually listed for revision. The method name itself tells what it needs to do. Though different approaches is tested just to validate that XML file. The one shown above gets the job done. But I know it is still an ugly work. Its just a simple logic that checks elements name and attributes. Values are ignored and might cause error if in case the XML saved file is changed. Especially its values.

Update’s to this code or to this app will be posted in separate articles. For now let’s jump back to our XmlLoader Load method.

Completing XmlLoader Load Method


public void Load(string filepath) {
      try {
            document = XDocument.Load(filepath);

            if (!IsSavedFileValid(document)) {                
                  MessageBox.Show("Invalid saved file.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
            }

            progress = new Progress();

            try {
                  var root = document.Element(rootElement);

                  if (root.HasAttributes) {
                        DateTime startOftime;
                        DateTime.TryParse(root.Attribute(rootAttributesTime).Value.Trim(), out startOftime);
                        progress.StartOfTime = startOftime;

                        progress.Rank = root.Attribute(rootAttributesRank).Value.Trim();
                  } else {
                        MessageBox.Show("Problem loading attributes.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                  }

                  if (root.Element(mainElement) == null) {                    
                        MessageBox.Show("Progress data not found.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        return;
                  }

                  var exercises = root.Elements();
                  if (exercises != null) {
                        progress.GetExercises = new List();
                        foreach (var exercise in exercises)
                        {
                            progress.GetExercises.Add(exercise);
                        }
                  } else {
                        MessageBox.Show("Problem retrieving exercises data.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                  }
            } catch {
                  MessageBox.Show("Problem retrieving saved data.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
      } catch(Exception ex) {
            MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);                
      }
}

Inside our Load method. We create a new Progress object after we passed the IsSavedFileValid check.


progress = new Progress();

Remember our Progress object holds the properties we need to see in our form. Every time we get a data in our xml file. We will assign in to a progress property. It will hold all the data we get from our saved file.

Try and Catch block inside Try and Catch Block

In the code above, we used the first try and catch block to handle exceptions when loading an XML file. The try and catch block inside will handle errors thrown while reading the saved file. This will catch all the errors thrown each time an element or an attribute doesn’t exist.


var root = document.Element(rootElement);

The code above points to our xml file root element(“Training”). Also we provided a couple of if-else statements that will show user-friendly errors each time we failed on getting data from our saved file. The code below shows an if-else statement that checks if an attribute exist in the root element.


if (root.HasAttributes) {
	DateTime startOftime;
	DateTime.TryParse(root.Attribute(rootAttributesTime).Value.Trim(), out startOftime);
	progress.StartOfTime = startOftime;
	
	progress.Rank = root.Attribute(rootAttributesRank).Value.Trim();
} else {
	MessageBox.Show("Problem loading attributes.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}

The code above only checks if attributes exist in the element. Its not specifically checking for both needed Day and Rank attributes. In case one attribute is missing, it will not show an error. But it will still throw an exception because we are still inside the try and catch block.

We can try to create a much cleaner code but we will not put this in this series. It might be posted in a different article or post.

TryParse Method

To get our current Progress StartOfTime I used the DateTime.TryParse method. This checks if the XML root attribute StartOfTime value has a valid DateTime data. The method also converts and returns a DateTime value if the conversion succeeded. It will return a boolean value false if it fails.

Pointing to Progress Element


if (root.Element(mainElement) == null) {                    
	MessageBox.Show("Progress data not found.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
	return;
}

Now, the code above checks if the root “Training” element has an element named “Progress” inside. Remember we declared the variable mainElement = “Progress” in our earlier post. The if-else statement has a return statement that is triggered whenever the “Progress” element doesn’t exist in the XML file.

Getting all Exercises Data from XML File


var exercises = root.Elements();
		
if (exercises != null) {
	progress.GetExercises = new List();
	foreach (var exercise in exercises) {
		progress.GetExercises.Add(exercise);
	}
} else {
	MessageBox.Show("Problem retrieving exercises data.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}

The remaining code above adds all the element to a new list of XElement in the Progress object. It also checks if the current collection (“exercises”) isn’t null so that we can loop through all the elements and add it to the newly created list.

If all runs smoothly, our Progress class will now be loaded with needed data. We can now access its value and display it in our form.

Leave a Reply