I am writing some code to parse an xml file of the following format (truncated for simplicity)
<?xml version="1.0" encoding="UTF-8"?>
<ship name="Foo">
<base_type>Foo</base_type>
<GFX>fooGFX</GFX>
....
</ship>
I am using a dictionary consisting of key pairs of parameter and and xpath to value, and querying this to load all the values into various variables. However, I am noticing that there will be a massive amount of code duplication. In fact, for every value I wish to retrieve, I will have to write another line of almost identical looking code. Here is my code:
class Ship
{
public Ship()
{
paths = new Dictionary<string, string>();
doc = new XmlDocument();
//Define path to various elements
paths.Add("name", "/ship/@name");
paths.Add("base_type", "/ship/base_type");
paths.Add("GFX", "/ship/GFX");
}
public void LoadFile(string filename)
{// Loads the file and grabs the parameters
doc.Load(filename);
Name = doc.SelectSingleNode(paths["name"]).Value;
Base_type = doc.SelectSingleNode(paths["base_type"]).Value;
GFX = doc.SelectSingleNode(paths["GFX"]).Value;
}
public Dictionary<string, string> paths; //The XPaths to the various elements, define them in constructor
public XmlDocument doc;
public string Name;
public string Base_type;
public string GFX;
}
Notice the duplication here:
variable = doc.SelectSingleNode(paths["variable_name"]).value.
There are going to be many more variables, so this section will be massive.
Is there anyway to simplify this? If this was C++, I would probably try pointers, but I know they are not recommended for use in C#, so is there a similar way?
I would be looking for something that I could give a list of variable names and xpaths, and have the code pull out all the values and load them in the variable in some kind of loop or something. I want to use XPaths because I expect that the format of this file may change periodically.
Any ideas?
Thanks in advance.
EDIT: I would also like to be able to modify this data and save it back. I am not adverse to saving an entire new tree if necessary, but it would be nice to modify the data in place if possible. I don't need a solution for the modification, but I just need to have this option open.
One good way to achieve the population of the object is by using XmlSerializer.Deserialize().
Something like this:
namespace TestSerialization
{
using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
public class TestSerialization
{
static void Main(string[] args)
{
string theXml =
@"<ship name='Foo'>
<base_type>Foo</base_type>
<GFX>fooGFX</GFX>
</ship>";
Ship s = Ship.Create(theXml);
// Write out the properties of the object.
Console.Write(s.Name + "\t" + s.GFX);
}
}
[XmlRoot("ship")]
public class Ship
{
public Ship() { }
public static Ship Create(string xmlText)
{
// Create an instance of the XmlSerializer specifying type.
XmlSerializer serializer = new XmlSerializer(typeof(Ship));
StringReader sr = new StringReader(xmlText);
XmlReader xreader = new XmlTextReader(sr);
// Use the Deserialize method to restore the object's state.
return (Ship)serializer.Deserialize(xreader);
}
[XmlAttribute("name")]
public string Name;
[XmlElement("base_type")]
public string Base_type;
public string GFX;
}
}
UPDATE: The OP has added an additional question:
I would also like to be able to modify this data and save it back. I am not adverse to saving an entire new tree if necessary
Just use the XmlSerializer.Serialize()
method.
Here is a typical example of using it:
// Create an XmlTextWriter using a FileStream.
Stream fs = new FileStream(filename, FileMode.Create);
XmlWriter writer =
new XmlTextWriter(fs, Encoding.Unicode);
// Serialize using the XmlTextWriter.
serializer.Serialize(writer, yourObject);
writer.Close();