Xml Serialisation

In this article, we will discuss further about saving information. In the previous article, we were saving data on the device using PlayerPrefs. Even though it is more than fine, it is not known by other frameworks how the data is saved. If we want to transfer our data, and particularly if the receiver is not a Unity application (server, other apps), it is required to use a format that is commonly understood without requiring a special parser. You could come up with your own parser but the other end may not want to use it. Xml or json are commonly used. We will look at xml in here.

What is xml and why do we use it

xml introduction is the best place to learn about xml so we can go further.
When you save data, whatever the extension, it always boils down to a text or a binary file. The only difference is that you can open a text file with any basic editor like notepad. A Xml file is a just a text file with a specific extension. The extension is only there to inform the OS that this is a xml file so it should be open with a software that understands xml. Often, companies use a basic file type, only the extension is modified. For example, plist is a xml file that will open with xcode by default.

So, if a xml file is just a text file, it might as well be used as such. A xml file not only get a specific extension but also a specific formatting. That is what makes it so special, because there are hundreds of parser that are implemented to be able to parse a file with all the specifications of a xml file. For instance, here is a really basic xml file:

<?xml version="1.0" encoding="UTF-8"?>
<library>
  <movie title="Titanic">
    <date>1997-05-06</date>
    <actors>
      <actor>Leonardo Di Caprio</actor>
      <actor>Billy Zane</actor>
    </actors>
  </movie>
</library>

So it looks like HTML and it is actually based on it. There is always an opening and a closing tag called the root. You can have as many tags as you want nested into one tag. Only common sense will stop you there. Each tag can contain attributes like the title in our example or an element like the date. Remember the terms root, attribute and element as they are coming back soon.

If your text file comply with those constraints, then you have a valid xml file. You can change the extension and use a xml parser.

Many internet communications use xml, though json is coming strong for various reasons, Xml is still a choice when it comes to build script.

  • plist for iOS
  • manifest or resource folders on Android
  • build files of your Unity project
  • Build script  build automation system like Ant
  • Many others….

Xml offers two types of data, the attributes and the elements. In our example, the title is an attribute while the others are all elements. An attribute is always placed between quotation and cannot contain sub element. Consider the actors element, it has child elements, and themselves they could contain child elements. An attribute cannot contain child attribute or element. Also, too many attributes alter quickly readability. Apart from those two cases, there is nothing telling you why and where to use one or the other. It is all up to you.

Defining the class

For the rest of this article, we will define a xml file. We want to use the content of it but we cannot just keep it as a string. So we will convert the xml content into a class that will reproduce the tree of the xml file.
Since we worked a leaderboard in the previous article, let’s create a xml file of it:

<leaderboard>
    <racer name =" Michael Winslow">
      <car>Mercedes</car>
      <point>125</point>
      <race name ="Spa Francorchamp">
        <position>3</position>
        <date>2005-02-04</date>
      </race>
    <race name ="Magny Cours">
        <position>5</position>
        <date>2005-06-09</date>
      </race>
    </racer>
    <racer name ="Steve Guttenberg">
      <car>Lada</car>
      <point>130</point>
      <race name ="Spa Francorchamp">
        <position>1</position>
        <date>2005-02-04</date>
      </race>
      <race name ="Magny Cours">
        <position>1</position>
        <date>2005-06-09</date>
      </race>
    </racer>
   <racer name ="Georges Gaynes">
      <car>Peugeot</car>
      <point>102</point> 
      <race name ="Spa Francorchamp">
        <position>1</position>
        <date>2005-02-04</date>
      </race>
   <race name ="Magny Cours">
        <position>2</position>
        <date>2005-06-09</date>
      </race>
    </racer>
</leaderboard>

So we need to convert that into C# classes:

public class Leaderboard
{
    public Racer[] racers;
}
public class Racer 
{
    public string name;
    public string car;
    public int point;
    public Race[] races;
}
public class Race 
{
    public string name;
    public DateTime date;
    public int position;
}

This is a code representation of our xml file. It is possible to parse the code with XmlReader and other classes for Xml, in this case, the user would have to get the parent node and then the child. The child would become a parent to get its own child and so on. It tends to make code difficult to read and maintain because you need to follow the hierarchy to figure out how to get a node deep down in the tree. It is not impossible but there is way easier. Using XmlSerializer, we can use C# attributes (not the xml attribute we talked about) to decorate our classes and members and most of the work is done by the framework.

using System;
using System.Xml.Serialization;

[Serializable]
[XmlRoot("leaderboard")]
public class Leaderboard
{
    [XmlElement("racer")]
    public Racer[] racers;
}
[Serializable]
public class Racer 
{
    [XmlAttribute]
    public string name;
    [XmlElement("car")]
    public string car;
    [XmlElement("point")]
    public int point;
    [XmlElement("race")]
    public Race[] races;
}
[Serializable]
public class Race {
    [XmlAttribute]
    public string name;
    [XmlElement("date")]
    public DateTime date;
    [XmlElement("position")]
    public int position = -1;

}

The Serializable attribute allows to see the class in the editor and will allow us to serialize/deserialize (remember the previous article required the attribute). The Leaderboard class get a root attribute since it works as the root of the file. Notice the difference of C# attribute between element and Xml attribute. The name variable are xml attributes and the system will be able to find them and assign them to the corresponding variable. Make sure the variable naming matches.

As for elements, the naming of the variable does not matter, only the parameter of the attribute has to match the naming in the xml file.

It is possible to give default value. The position member gets a -1 value if the position element is not found in the xml file.

The Serializable attribute is not required when you use XmlElement/Attribute as it is already contained in those.

How to retrieve data

We only need now to get the file and use it. Save your xml file and name it Test.xml. Then drag it in the Assets folder of your project. You can place it where you want even on a server, as long as you know the path.

 
[SerializeField]
private Leaderboard board= null;
void Start () 
{
    string path = Path.Combine(Application.dataPath, "Test.xml");
    XmlSerializer serializer = new XmlSerializer(typeof(Leaderboard));
    string xml = File.ReadAllText(path);
    using(var stream = new MemoryStream(Encoding.UTF8.GetBytes(xml)))
    {
        board = (Leaderboard)serializer.Deserialize(stream);
    }
}

And this is it. Your board object contains the whole xml file. You can even see it in the inspector.

Storing data

We have the data ready to be used and we are able to modify them.
So we want to add 100 points to the first racer and update our xml file.

[SerializeField]
private Leaderboard board = null;
private void Start()
{
    private void Start () 
    {
        string path = Path.Combine(Application.dataPath, "Test.xml");
        XmlSerializer serializer = new XmlSerializer(typeof(Leaderboard));
        string xml = File.ReadAllText(path);
        using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(xml)))
        {
            board = (leaderboard)serializer.Deserialize(stream);
        }
    
        // Adding 100 points to the first racer
        board.racers[0].point += 100; 
        using(TextWriter text = new StreamWriter(path,false,Encoding.ASCII))
        {
            serializer.Serialize(text, car);
        }
}

And then again, this is it. We use a TextWriter stream with the path to our xml file, the second parameter indicates we do not wish to append, so we overwrite and finally as ascii. The serializer will do the rest based on how the classes were implemented.

Adding items

You want to add a string field for nickname.

[Serializable]
public class Racer {
    [XmlAttribute]
    public string name;
    [XmlElement("car")]
    public string car;
    [XmlElement("point")]
    public int point;
    [XmlElement("nickname")]  
    public string nickname = "Teukka Salama";  // New member
    [XmlElement("race")]
    public Race[] races;
}

In this example, the nickname was given by default to all racer, but you can then change it in code. The tag and content are automatically added to the xml file.
This also tells us that we can build a new xml file from scratch with C#, without bothering too much about missing a closing tag or placing the element in the wrong hierarchy.

Conclusion

This is it for xml, you now have most of the tool to get and create xml file. For the time being, it is still stored locally, but you could send it to a server so that a php script would parse it to a database.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s