Lesson 14.1: Moving game data to external files

In this lesson, we’ll start moving the game data to XML files, and change the factories to read from them.

After we do this, if you want to add more locations, monsters, items, quests, and recipes, you will only need to update these data files. You won’t need to modify the source code and rebuild the solution.

There will be three lessons to convert the ItemFactory to use XML data files, because I want to cover three new topics. This lesson will cover reading the XML file and using it to create objects. The next two will cover “generics” and “extension methods”.

Then, there will be a final lesson to convert all the other factories to read data from XML files.




Lesson Steps

Step 1: Create a new GameData folder in Engine

This is where we’ll add the game data files.


Step 2: Create Engine\GameData\GameItems.xml

To create this file, right-click on the GameData folder and select Add -> New Item… -> Visual C# Items -> Data -> XML File.


Select GameItems.xml in the Solution Explorer and view its properties. Set the value of “Copy to Output Directory” to “Copy Always”.


Now, when you build the program, Visual Studio will create a GameData folder in the same folder as the executable files and copy the GameItems.xml data file into the it.


XML (Extensible Markup Language) is a format for storing (or passing) data.

We’re already using XAML (a specialized form of XML) to define controls in the UI. Our data files will follow a similar style: opening and closing tags, attributes, and the possibility of an element having child elements.

In the Items.xml file (shown below), the first line is the “XML declaration” line. It tells what version of XML is being used, and what character set is being used. I’m using “utf-8” for the encoding. It’s the most-common encoding.


The “GameItems” node/element is the root element – all the other elements in this file are its children, because they are in between the “<GameItems>” opening tag and the “</GameItems>” closing tag.

GameItem’s three child elements are “Weapons”, “HealingItems”, and “MiscellaneousItems”. Each of those elements has their own child elements – one for each game item object.

We store the item’s property values as attributes. When we get to objects that have List properties (like the Ingredients property in the Recipes class), we will make an element for each recipe, which will have a child element for each ingredient.


Another popular format is JSON (JavaScript Object Notation). If you ever want to use JSON in a .NET program, you will probably want to include the Newtonsoft.Json NuGet package in your project, for its helper functions.



<?xml version="1.0" encoding="utf-8" ?>
    <Weapon ID="1001" Name="Pointy stick" Price="1" MinimumDamage="1" MaximumDamage="2"/>
    <Weapon ID="1002" Name="Rusty sword" Price="5" MinimumDamage="1" MaximumDamage="3"/>
    <Weapon ID="1501" Name="Snake fang" Price="0" MinimumDamage="0" MaximumDamage="2"/>
    <Weapon ID="1502" Name="Rat claw" Price="0" MinimumDamage="0" MaximumDamage="2"/>
    <Weapon ID="1503" Name="Spider fang" Price="0" MinimumDamage="0" MaximumDamage="4"/>
    <HealingItem ID="2001" Name="Granola bar" Price="5" HitPointsToHeal="2"/>
    <MiscellaneousItem ID="3001" Name="Oats" Price="1"/>
    <MiscellaneousItem ID="3002" Name="Honey" Price="2"/>
    <MiscellaneousItem ID="3003" Name="Raisins" Price="2"/>
    <MiscellaneousItem ID="9001" Name="Snake fang" Price="1"/>
    <MiscellaneousItem ID="9002" Name="Snakeskin" Price="2"/>
    <MiscellaneousItem ID="9003" Name="Rat tail" Price="1"/>
    <MiscellaneousItem ID="9004" Name="Rat fur" Price="2"/>
    <MiscellaneousItem ID="9005" Name="Spider fang" Price="1"/>
    <MiscellaneousItem ID="9006" Name="Spider silk" Price="2"/>


Step 3: Modify Engine\Factories\ItemFactory.cs

Now, we’ll change the ItemFactory class to read from the GameItems.xml file.


We need to add three new “using” statements, since we are using code from new namespaces (for the XML, reading the file, and converting values). Add:

using System;
using System.IO;
using System.Xml;


On line 13 is a constant to hold the data file name. The single “.” means, “start looking for this file from the current directory” (the directory where the program will be running.

We need to double the “\” characters, because a single “\” in a string is normally used to insert special characters, like tabs, carriage returns, and line feeds. This is called an “escape sequence“. By having “\\” C# knows we just want to include the “\” character in the string.


I’ve changed the ItemFactory function from the hard-coded values. Now, it checks if the data file exists. If it does, the function creates an XmlDocument object, reads all the text from the data file, and loads the data into the XmlDocument object. Now we can use the XML functions to retrieve data from the XML elements.

This calls the LoadItemsFromNodes function that accepts a list of XML nodes (elements). The parameter passed into “SelectNodes” is the XPath of the nodes we are looking for in the XmlDocument.

So, “/GameItems/Weapons/Weapon” finds all the nodes named “Weapon” that are children of the node named “Weapons”, which is a child of the node named “GameItems”.


The LoadItemsFromNodes function (lines 44-78) loops through each node in the passed-in parameter (an XmlNodeList object), gets the values from the node’s attributes, and creates an object to add to the _standardGameItems variable.


DetermineItemCatagory (lines 80-91) looks at the Name property of the current node to determine the value for the object’s ItemCategory property.


To read the attribute values, there are three new functions on lines 93-113: GetXmlAttributeAsInt, GetXmlAttributeAsString, and GetXmlAttribute.

GetXmlAttribute looks in the node’s Attributes collection (line 105) for the attribute with the name we’re looking for. If it finds it, it returns the attribute’s Value as a string (line 112).

The two extra functions are to get the values back as either a string or an integer. We’re going to clean this up in the upcoming lesson on generics.


The rest of the code in LoadItemsFromNodes reads values from the node’s attributes and uses them to populate the GameItem object’s properties.

On lines 62-68, we build the AttackWithWeapon action for weapons. On lines 69-74, we build the Heal action for healing items.



using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;
using Engine.Actions;
using Engine.Models;

namespace Engine.Factories
    public static class ItemFactory
        private const string GAME_DATA_FILENAME = ".\\GameData\\GameItems.xml";

        private static readonly List<GameItem> _standardGameItems = new List<GameItem>();

        static ItemFactory()
                XmlDocument data = new XmlDocument();

                throw new FileNotFoundException($"Missing data file: {GAME_DATA_FILENAME}");

        public static GameItem CreateGameItem(int itemTypeID)
            return _standardGameItems.FirstOrDefault(item => item.ItemTypeID == itemTypeID)?.Clone();

        public static string ItemName(int itemTypeID)
            return _standardGameItems.FirstOrDefault(i => i.ItemTypeID == itemTypeID)?.Name ?? "";

        private static void LoadItemsFromNodes(XmlNodeList nodes)
            if(nodes == null)

            foreach(XmlNode node in nodes)
                GameItem.ItemCategory itemCategory = DetermineItemCategory(node.Name);

                GameItem gameItem =
                    new GameItem(itemCategory,
                                 GetXmlAttributeAsInt(node, "ID"),
                                 GetXmlAttributeAsString(node, "Name"),
                                 GetXmlAttributeAsInt(node, "Price"),
                                 itemCategory == GameItem.ItemCategory.Weapon);

                if(itemCategory == GameItem.ItemCategory.Weapon)
                    gameItem.Action =
                        new AttackWithWeapon(gameItem,
                                             GetXmlAttributeAsInt(node, "MinimumDamage"),
                                             GetXmlAttributeAsInt(node, "MaximumDamage"));
                else if(itemCategory == GameItem.ItemCategory.Consumable)
                    gameItem.Action =
                        new Heal(gameItem,
                                 GetXmlAttributeAsInt(node, "HitPointsToHeal"));


        private static GameItem.ItemCategory DetermineItemCategory(string itemType)
                case "Weapon":
                    return GameItem.ItemCategory.Weapon;
                case "HealingItem":
                    return GameItem.ItemCategory.Consumable;
                    return GameItem.ItemCategory.Miscellaneous;

        private static int GetXmlAttributeAsInt(XmlNode node, string attributeName)
            return Convert.ToInt32(GetXmlAttribute(node, attributeName));

        private static string GetXmlAttributeAsString(XmlNode node, string attributeName)
            return GetXmlAttribute(node, attributeName);

        private static string GetXmlAttribute(XmlNode node, string attributeName)
            XmlAttribute attribute = node.Attributes?[attributeName];

            if(attribute == null)
                throw new ArgumentException($"The attribute '{attributeName}' does not exist");

            return attribute.Value;


Step 4: Test the game


Return to main page

9 thoughts on “Lesson 14.1: Moving game data to external files

  1. Hello Scott,

    First off, your lessons are fantastic, I love them!

    Second, my code doesn’t recognize ‘XmlDocument’, (with ‘using System.Xml’). Apparently I selected .NET Core instead of Standard? I’m pretty sure I selected Standard but fine, I guess. (Rest of the lessons and program works perfectly fine, just not this bit :<)

    If you know a quick fix, let me know. I'm gonna try and add the Standard package to the solution, that's what's suppose to fix it according to the internet.

    Anyway, thank you very much so far, I hope I can continue following this, I'm really enjoying it!

    1. Thanks Tim!

      For the Engine project, use a Class Library (.NET Framework) project under the Windows Desktop section (see the image below)

      Let me know if you have any problems.

      1. Sorry for all the spammy messages; didn’t realize it’d take so long for the comment to show >.<

        Soo, I broke the project by trying to do what you suggested, then I reverted back to the previous state and now it works…

        So, thanks for the help, I'm gonna quickly do this lesson again and catch up! Can't wait for the new lesson, but take your time 🙂

          1. Hello Scott,

            The first problem seems to be fixed, the program accepts the ‘XmlDocument’ variable, however, it doesn’t like the ‘data.LoadXml’ part now, saying I need to add a reference to ‘mscorlib v4.0.0.0’. The ‘Potential Fix’ it recommends does nothing, sadly. I tried adding a reference(to mscorlib v2 and v4) for the Engine project but that did nothing as well.

            I guess there’s no other choice then to start over and hope this doesn’t happen again.

            Thanks for the help either way!

          2. That’s one of the problems with .NET Core and .NET Standard projects – they don’t have everything available yet. Starting with the .NET Framework project should fix the problem.

  2. Got it, I will start the lessons over, using the .Net Framework project. Again, thanks for the help so far, hope your own work calms down soon so you can make some more lessons because they are awesome! 🙂

  3. I have been messing with this program for awhile and I just have a question about the item constructor. If I wanted different items, like using magic spells. How would I be able to access things like ‘mana cost’ on the XAML? I was able to if I passed these fields into the constructor, but with all the different types of items it was getting out of hand. Do I need to make different constructors for the different item types or is there an easier way to do this? Thanks for all the help and the videos are great!

    1. You’re welcome, Matt!

      If your classes are very different, with many different properties, then you probably will need different constructors.

      When you display the different objects in XAML, try creating a Data Template for each class item. Then, you can create a DataTemplateSelector to choose the data template to use when you want to display the object in the XAML screen.

Leave a Reply

Your email address will not be published. Required fields are marked *