50 Unity Tips #10: XML Serialization
Unity UnityTipsTricksXML (Extensible Markup Language) is a human-readable and machine-parsable markup language which defines a set of rules for encoding a document. C# has built-in XML serialization with the following noteworthy points:
- Need to import System.Xml.Serialization which adds about 1MB to the final build.
- Like JSON, XML can only serialize public fields.
- The serializable class must have a parameterless constructor.
XML Format
Like JSON strings, XML documents are human-readable and contain:
- Tags, both start-tags
and end-tags - Elements, the contents between two tags, which may contain child elements for instance <enemy>Big Boss</enemy> or <level><numberOfLives>3</numberOfLives></level>
- Attributes, name-value pairs contained within the start tag, for instance <level index=0>
XML Serialization Attributes
[XmlElement] indicates that a field will be represented as an XML element. By default all public fields are treated as elements, with their name as the tag, so the only real practical use of [XmlElement] is to change the tag from the default value.
|
<PlayerData> <n>Gordon Freeman</n> </PlayerData> |
[XmlAttribute] indicates that the field will be represented as an XML attribute
|
<PlayerData name="Gordon Freeman"> </PlayerData> |
[XmlIgnore] is used to skip the serialization of a public field.
|
<PlayerData> <name>Gordon Freeman</name> </PlayerData> |
[XmlRoot] is used to alter the document’s root tag (which by default is the object’s class name)
|
<Player> <name>Gordon Freeman</name> </Player> |
[XmlArray] is used to specify the array’s tag, while [XmlArrayItem] is used to specify the individual element’s tag.
|
<PlayerData> <name>Gordon Freeman</name> <scores> <score>50</score> <score>76</score> <score>19</score> </scores> </PlayerData> |
Saving/Loading Classes/Structs to Disk
Like the Binary and JSON Serialization tips, given the PlayerData class
[System.Serializable]
public class PlayerData
{
public string name;
public int score;
[System.NonSerialized] private int tempValue;
private int somePrivateVariable;
}
and using an XMLSerializer (which could be better named)
using System.Xml.Serialization;
public static T Load<T>(string filename) where T: class
{
string path = PathForFilename(filename);
if(XMLSerializer.PathExists(path))
{
try
{
using(Stream stream = File.OpenRead(path))
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
return serializer.Deserialize(stream) as T;
}
}
catch(Exception e) { Debug.LogWarning(e.Message); }
}
return default(T);
}
public static void Save<T>(string filename, T data) where T: class
{
string path = PathForFilename(filename);
using(Stream stream = File.OpenWrite(path))
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
serializer.Serialize(stream, data);
}
}
we can easily save our data to file and reload it
string filename = "PlayerData.xml";
PlayerData data;
if(XMLSerializer.FileExists(filename))
{
data = XMLSerializer.Load<PlayerData>(filename);
}
else
{
data = new PlayerData();
XMLSerializer.Save<PlayerData>(filename, data);
}
Debug.Log(data.ToString());
As per JSON, somePrivateVariable is not serializable and the resulting XML output is human readable and thus potentially player-modifiable. Nevertheless, XML is commonly used for saving data and game states - one approach is to encyrpt the file using System.Security.Cryptography.
ToXML, FromXML
Like JSON, we can easily convert an object to an XML string and back again using the following methods:
public static string ToXML<T>(T data) where T : class
{
using(StringWriter textWriter = new StringWriter())
{
XmlSerializer serializer = new XmlSerializer(data.GetType());
serializer.Serialize(textWriter, data);
return textWriter.ToString();
}
}
public static T FromXML<T>(string xml) where T : class
{
//load the xml into an xml document to remove the header
XmlDocument xmlDoc = new XmlDocument(); xmlDoc.LoadXml(xml);
if(xmlDoc.FirstChild.NodeType == XmlNodeType.XmlDeclaration) { xmlDoc.RemoveChild(xmlDoc.FirstChild); }
using(Stream stream = ToStream(xmlDoc.InnerXml))
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
return serializer.Deserialize(stream) as T;
}
}
This is something I haven’t really tested, but it seems to work :p Could be useful saving an object to PlayerPrefs or pulling objects from RemoteSettings.
XML vs JSON
One could argue that JSON is more user-readable as it isn’t cluttered with tags, which could benefit non-technical people who may need to edit the files. JSON built-in support isn’t as encompasing as XML, so, for instance, dictionaries and top-level arrays will need their own custom serializers. XML requires no custom code or formatting, but System.Xml.Serialization will add 1MB to the final build.
Example
Scriptable Objects are a great way to vary certain parameters during gameplay and then persist to the project. However what if the Game Designer is not comfortable with Unity’s interface? Then using a standalone build and an XML database is a one option - an XML file can be easily downloaded at runtime and deserialized into a game object.
Conclusion
Hopefully this series of five posts on serialization was useful. Next week we’ll talk about UI tips, import settings, caching and more!
This post was generated from a GitHub repository.