Skip to content

Special cases

Indraneel Mahendrakumar edited this page Aug 7, 2022 · 6 revisions

While Serializer is very general in nature and works for most types of objects, there are certain types that it just cannot (de)serialize due to their fundamental nature. Examples of such types are unmanaged types (string, char, bool, numeric types like int, float, etc), collections, arrays, and so on.

GDSerializer handles the (de)serialization of these types by utilising more specialized implementations of ISerializer, designed to work for very specific types only.
Serializer knows about these specific serializer implementations, and so whenever it encounters a type that it cannot (de)serialize, it will automatically delegate the job to a more suitable serializer type.

Unmanaged types

The SimpleSerializer class handles (de)serialization of most unmanaged types. This includes:

  • string
  • char
  • bool
  • sbyte
  • byte
  • short
  • ushort
  • int
  • uint
  • long
  • ulong
  • float
  • double
  • decimal

All of these are simply serialized as a single text node inside an element node, and deserialized by using the relevant Parse() method.
For instance, the string "hello world" would be serialized as:

<string>hello world</string>

The number 99 (int) would be serialized as:

<int>99</int>

and so on.

Collections

Collections differ from most other types in the sense that they hold references to multiple other objects (usually of the same type). This means that Serializer cannot be used to (de)serialize them, as it will have no way of iterating over the collection, or treating child XML nodes as separate items belonging to the collection.

The CollectionSerializer class therefore handles (de)serialization of collections. More specifically, it can serialize and deserialize most types that implement ICollection<T>.

Collections will be serialized as an XML node with multiple child nodes named item. For example, a List<string> containing the strings "hello" and "world" would be serialized as follows:

<Collection Type="System.Collections.Generic.List&lt;string&gt;">
    <item>hello</item>
    <item>world</item>
</Collection>

Arrays

Arrays are a slightly more special kind of collection. Arrays do implement ICollection<T>, but it is not possible to call Add(T) on them as arrays have a fixed size. Moreover, arrays don't have a parameterless constructor (or any constructor for that matter) as they are special types.

The ArraySerializer class therefore handles (de)serialization of arrays.

Like collections, arrays will also be serialized as an XML node with multiple child nodes named item. For example, a string[] containing the strings "hello" and "world" would be serialized as follows:

<Array Type="string[]">
    <item>hello</item>
    <item>world</item>
</Array>

Dictionaries

Dictionaries are a slightly more special kind of collection. Technically, a type that implements IDictionary<TKey, TValue> also implements ICollection<KeyValuePair<TKey, TValue>>, but it cannot be (de)serialized by CollectionSerializer as KeyValuePair<TKey, TValue> has no parameterless constructor and no setters on its properties.

The DictionarySerializer class therefore handles (de)serialization of most types that implement IDictionary<TKey, TValue>.

Like collections, dictionaries will also be serialized as an XML node with multiple child nodes named item. However, each item node will have a child key node and a child value node, which will hold the key and value of the key-value pair. For example, a Dictionary<int, string> containing the pairs (1, "one"), (2, "two"), and (3, "three") would be serialized as follows:

<Dictionary Type="System.Collections.Generic.Dictionary&lt;int,string&gt;">
    <item>
        <key>1</key>
        <value>one</value>
    </item>
    <item>
        <key>2</key>
        <value>two</value>
    </item>
    <item>
        <key>3</key>
        <value>three</value>
    </item>
</Dictionary>

Note that if a field or property of an object is declared simply as an IDictionary<TKey, TValue>, or typeof(IDictionary<TKey, TValue>) is passed to the serializer as the optional type argument, it will (de)serialize using Dictionary<TKey, TValue> as the concrete type.

Enumerables

Not to be confused with enum.

In many cases, it may be desirable to expose a field or property as an IEnumerable<T> rather than a collection or a more concrete type. In this case, CollectionSerializer cannot help again - but EnumerableSerializer can.

If a field or property is declared simply as an IEnumerable<T>, or typeof(IEnumerable<T>) is passed to the serializer as the optional type argument, it will automatically (de)serialize it as a List<T>. In other words, members declared simply as IEnumerable<T> will be treated as List<T>.

Enums

Not to be confused with enumerables (IEnumerable<T>).

Enums are special cases of integers with names associated with them. An enum is technically an unmanaged type, but is not handled by SimpleSerializer. Instead, the EnumSerializer class handles the (de)serialization of enums.

An enum is serialized in XML as the string representation of its integer value. For example, the value Day.Tuesday in enum Day { Monday, Tuesday, ..., Sunday } would be serialized as follows:

<Day>Tuesday</Day>

Nodes

Most Godot nodes already fit the requirements to be (de)serialized by the default ISerializer implementation - Serializer. However, this does not allow their children to be (de)serialized, as there is no writeable property or field in a Node to set its children.

Therefore, the NodeSerializer class handles the (de)serialization of nodes. Everything is done as if it was being (de)serialized by Serializer - except for a node's children, which are (de)serialized using the <Children> tag. For example, a Sprite node named "Player" with a Tween and an Area2D named "Hitbox" as its children would be serialized like so:

<Godot.Sprite>
    <Name>Player</Name>
    <Children>
        <item Type="Godot.Tween"/>
        <item Type="Godot.Area2D">
            <Name>Hitbox</Name>
        </item>
    </Children>
</Godot.Sprite>

Summary

  • Certain types are special and cannot be (de)serialized through normal methods
    • Specialized serializers are implemented to handle these types
    • Serializer knows of the specialized serializers and will switch to using the appropriate one when it encounters such a type
      • This ensures that Serializer can be used everywhere without worrying about special cases as they will automatically be handled
  • Collections are (de)serialized by CollectionSerializer
    • Each item in the collection goes in an item node in the collection's XML node
  • Dictionaries are (de)serialized by DictionarySerializer
    • Each key-value pair in the dictionary goes in an item node, which has a key node and a value node
  • Enumerables (not enums) are (de)serialized by EnumerableSerializer
    • They are treated as lists
    • Like collections, each item goes in an item node
  • Enums are (de)serialized by EnumSerializer
    • The string representation of the corresponding enum integer value is used
  • Godot nodes are (de)serialized by NodeSerializer
    • All properties and fields are handled normally (as default)
    • The children of a node go in the Children tag, which is treated as a collection of nodes
Clone this wiki locally