Serialization Overview - TheShubham99/Terasology GitHub Wiki
Introduction
Terasology relies on a custom system to serialize Java objects of arbitrary types. It was initially written to serve as a medium to serialize objects of custom module types in components, but it has now been expanded to be a fully independent serialization library capable of serializing almost every type out-of-the-box.
Design
Serialization functionality is based around 3 main types: TypeHandler
, TypeHandlerFactory
and TypeHandlerLibrary
. More information can be found in the classes' JavaDoc.
TypeHandler
A TypeHandler
is responsible for serializing objects of a certain type. During the serialization process, it must convert an object to a PersistedData
(the serialized form represented in memory) such that a similar object with the same data can be accurately reconstructed from that PersistedData
.TypeHandler
code should be designed to be as robust as possible; rather than throwing an exception or returning null when a field is not serializable, a TypeHandler
should simply skip over the unserializable/unknown field and serialize the rest normally.
It is also important to note that TypeHandler
methods should be considered "hot" code, and any information that can be cached (like field metadata for a type) should be cached. Since the TypeHandler
s themselves are cached, this prevents unnecessary and potentially expensive information retrieval during the serialize and deserialize methods, which may be called multiple times during the serialization of a single object. This caching process is aided by TypeHandlerFactory
, which can provide the common cached data to the TypeHandler
during construction.
TypeHandlerFactory
A TypeHandlerFactory
is used to create a TypeHandler
for the given type. The TypeHandlerFactory
can obtain any information that the TypeHandler
might need (like class or field metadata) and pass it along to the TypeHandler
during construction.
Example of a TypeHandler
that does not require a TypeHandlerFactory
TypeHandlerLibrary
A TypeHandlerLibrary
is responsible for creating TypeHandler
s for various types. To obtain the TypeHandler
for a specific type, it iterates through all registered TypeHandlerFactory
s and asks each of them to create a TypeHandler
for the given type; the first successfully generated TypeHandler
is returned. The iteration is done so that factories that are registered later are given precedence over factories registered first. This allows consumers to override default type handling behaviour for specific types depending on their needs.
The TypeHandlerLibrary
caches type handlers for various types. Since a single type's TypeHandler
may be retrieved multiple times while serializing a single object, this cache helps avoid calling the expensive TypeHandler
creation process (via TypeHandlerFactory
) multiple times for efficiency. While the TypeHandlerLibrary
only deals with type handlers, it is also possible to "register" a TypeHandler
for a given type using addTypeHandler
-- under the hood, this method just creates a TypeHandlerFactory
.
The @RegisterTypeHandler
and the @RegisterTypeHandlerFactory
annotations can also be used to register type handlers and type handler factories to the TypeHandlerLibrary
in the Context
.
Examples
TypeHandler
without a TypeHandlerFactory
These are registered using the TypeHandlerLibrary.addTypeHandler
method.
TypeHandler
and TypeHandlerFactory
pair
Only the TypeHandlerFactory
has to be registered via the TypeHandlerLibrary.addTypeHandlerFactory
method; the TypeHandler
does not need to be registered separately.
Workflow
The workflow for (de)serializing an object is as follows:
- Retrieve the
TypeHandler
for its type from theTypeHandlerLibrary
(usinggetTypeHandler
) - (De)serialize the object using the retrieved
TypeHandler
.
Depending on the serialization format used, the TypeHandler
must be provided with appropriate PersistedData
and PersistedDataSerializer
instances. To make the process easier, Terasology provides the AbstractSerializer
utility class, implementations of which provide helper methods to easily serialize and deserialize objects to disk using the preferred format.
Supported Formats
Terasology currently supports JSON (via Gson) and binary (via Protobuf) serialization.
- To serialize objects in JSON, use the
GsonPersistedData
andGsonPersistedDataSerializer
classes or theGsonSerializer
utility class. - To serialize objects in binary, use the
ProtobufPersistedData
andProtobufPersistedDataSerializer
classes or theProtobufSerializer
utility class.
Features
- Format independent -- code that serializes an object remains the same regardless of the target format.
- Supports almost every possible type out-of-the-box.
- Supports recursive types (i.e. types that refer to themselves) but not cyclic object references.
- It is possible to override default serialization behavior by specifying a new
TypeHandler
that handles a type differently.