In order to minimize the size of the CAB size for the game, include the WM Smartphone 2003 as well as reduce the steps required to install the Mobile Kombat on the user’s devices, one of the main requirements was to use CF v1. We also decided not to use the SQL CE 2.0 for the same purposes. One of the data files used in the game was a set of questions and answers for the trivia on mobile programming. The obvious solution was to use DataSet for the data files, but don’t forget that it loads the whole Xml data file into memory and we were trying really hard to reduce a memory footprint of the game as much as possible. Let’s say we have 2 tables defined: Answer and Question. Here’s how the xml data looked like:
<NewDataSet>
<Question>
<QuestionID>1</< SPAN>QuestionID>
<Text>Which is a new .NET Compact Framework 2.0 namespace?</< SPAN>< SPAN>Text>
<CorrectAnswer>2</< SPAN>< SPAN>CorrectAnswer>
</< SPAN>< SPAN>Question>
<Question>
<QuestionID>2</< SPAN>< SPAN>QuestionID>
<Text>What operating system runs on Ultra Mobile PCs?</< SPAN>< SPAN>Text>
<CorrectAnswer>0</< SPAN>< SPAN>CorrectAnswer>
</< SPAN>< SPAN>Question>
…
<Answer>
<QuestionID>1< SPAN>QuestionID>
<Text>Sockets</< SPAN>< SPAN>Text>
</< SPAN>< SPAN>Answer>
<Answer>
<QuestionID>1< SPAN>QuestionID>
<Text>Collections< SPAN></< SPAN>Text>
</< SPAN>< SPAN>Answer>
<Answer>
<QuestionID>1< SPAN>QuestionID>
<Text>Cryptography</< SPAN>< SPAN>Text>
</< SPAN>< SPAN>Answer>
So I came up with the solution of creating a forward only lightweight XmlDataReader that would move from one xml node to another and will create an instance of the appropriate class based on the xml data. Take a look at the class diagram:

Well… you could say that XmlSerializer can just do that. But remember that we had to use the CF v1, which doesn’t provide the XmlSerializer. Considering the time constraints that would be required to create a generic XmlSerializer, my solution was that all classes would have to implement the IXmlDataFormat interface:
interface IXmlDataFormat
{
void ReadXml(XmlNodeList nodeList);
}
So the XmlDataReader when deserializing the objects will delegate the population of their properties to the object itself. Here’s how the object’s instance creation looks:
///
/// Creates an instance of the type if it implements the IXmlDataFormat interface.
///
/// The XmlNode.
///
private object GetValue(XmlNode node)
{
ConstructorInfo info = null;
// Make sure that the type implements IXmlDataFormat interface.
if (ImplementsInterface(type, typeof(IXmlDataFormat)))
{
info = type.GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, new Type[0], null);
if (info == null)
{
throw new ArgumentException("No constructor");
}
// Create an instance
IXmlDataFormat objValue = info.Invoke(null) as IXmlDataFormat;
if (objValue != null)
{
// Call the interface method
objValue.ReadXml(node.ChildNodes);
}
return objValue;
}
return null;
}
And here’s how an actual class would implement the IXmlDataFormat interface:
public void ReadXml(XmlNodeList nodeList)
{
foreach (XmlNode node in nodeList)
{
XmlElement element = node as XmlElement;
if (element != null)
{
switch (element.LocalName)
{
case "QuestionID":
{
this.questionID = Convert.ToInt32(element.InnerText);
continue;
}
case "Text":
{
this.text = element.InnerText;
continue;
}
case "CorrectAnswer":
{
this.correctAnswer = Convert.ToInt32(element.InnerText);
continue;
}
}
}
}
}
The code above just simply iterates through xml nodes and populates the object’s properties with the values from xml data.
I’ve also created the XmlDataManager class. This class implements a few useful helper methods that wrap calls to the XmlDataReader. Here’s some sample code that will retrieve all answers from xml data file by a certain question id:
// Create an instance of the XmlDataReader
XmlDataReader answersReader = new XmlDataReader(this.triviaFilePath, typeof(Answer));
// Create an instance of the XmlDataManager by passing the XmlDataReader
XmlDataManager answersManager = new XmlDataManager(answersReader);
// Retreive the list of anwers
IList answersList = answersManager.GetObjectList("QuestionID", QID);
You can download the sample project and the source code for the XmlDataReader here:
XmlDataReaderTest.zip (48.11 KB)