Table of Contents |
---|
...
Child pages (Children Display) |
---|
All classes deriving from ModelBase use the serialization engine of Catel to serialize itself in a whole or as a subset of properties. Below is a schema which sheds some light on the architecture.
...
Info |
---|
Workflow 1 represents the serialization. Workflow 2 represents the deserialization. |
Customizing serialization
Customizing the serialization for specific models
Catel has a default behavior for what gets serialized. It can be tweaked by including / excluding fields and properties by using the IncludeInSerialization and ExcludeFromSerialization attributes. But sometimes one needs more specific customization of the serialization for a specific type. This customization is possible via the ISerializerModifier.
Creating the modifier
To customize the serialization of a specific model type, one needs to implement the ISerializerModifier interface. The example belows shows how to encrypt the Password property on the Person model class.
Code Block |
---|
public class PersonSerializerModifier : SerializerModifierBase<Person>
{
public override void SerializeMember(ISerializationContext context, MemberValue memberValue)
{
if (string.Equals(memberValue.Name, "Password"))
{
memberValue.Value = EncryptionHelper.Encrypt(memberValue.Value);
}
}
public override void DeserializeMember(ISerializationContext context, MemberValue memberValue)
{
if (string.Equals(memberValue.Name, "Password"))
{
memberValue.Value = EncryptionHelper.Decrypt(memberValue.Value);
}
}
} |
Registering the modifier
To register a modifier for a specific class, define the SerializerModifier attribute:
Code Block |
---|
[SerializerModifier(typeof(PersonSerializerModifier))]
public class Person : ModelBase
{
// .. class contents
} |
Note |
---|
Note that modifiers are inherited from base classes. When serializing, the modifiers defined on the most derived classes will be called last. When deserializing, the modifies defined on the most derived classes will be called first. |
Serializing members using ToString / Parse
Sometimes types (classes or structs) don't implement a proper serialization mechanism. If they support proper ToString(IFormatProvider) and Parse(string, IFormatProvider) methods, there is no need to create a custom SerializerModifier to serialize these types. To let the serializers take care of this automatically, at least one of the following options must be true:
- The member is decorated using the SerializeUsingParseAndToString attribute
- The container class has a SerializerModifier that returns true in the ShouldSerializeMemberUsingParse method
Info |
---|
Note that decorating a member that does not implement proper ToString(IFormatProvider) and Parse(string, IFormatProvider) methods is useless, the serialization engine will ignore these types |
For example, the class below is an excellent usage example of when to use this technique:
Code Block |
---|
[Serializable, StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct Vector
{
public double X;
public double Y;
public double Z;
public Vector(double x, double y, double z)
{
X = x;
Y = y;
Z = z;
}
public string ToString(IFormatProvider formatProvider)
{
return $"{X.ToString(formatProvider)} {Y.ToString(formatProvider)} {Z.ToString(formatProvider)}";
}
public static Vector Parse(string value, IFormatProvider formatProvider)
{
var splitted = value.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries);
var x = double.Parse(splitted[0], formatProvider);
var y = double.Parse(splitted[1], formatProvider);
var z = double.Parse(splitted[2], formatProvider);
var vector = new Vector(x, y, z);
return vector;
}
} |
Customizing the serialization engines
Since the SerializerBase does all the heavy lifting, it is very easy to customize the behavior of an existing serializer or create a completely new one. Each serializer implements its own interface and are registered in the ServiceLocator using the following interfaces:
- XmlSerializer => IXmlSerializer
- BinarySerializer => IBinarySerializer
To customize a serializer, derive from an existing class and customize a method. The serializer below makes sure that specific members are never serialized. It keeps all other serialization logic intact.
Code Block |
---|
public class SafeXmlSerializer : XmlSerializer
{
protected override bool ShouldIgnoreMember(ModelBase model, PropertyData property)
{
if (model is SecurityModel)
{
if (string.Equals(property.Name, "Password"))
{
return true;
}
}
return base.ShouldIgnoreProperty(model, property);
}
} |
The only thing to do now is to register this custom instance in the ServiceLocator:
Code Block |
---|
ServiceLocator.Default . RegisterType<IXmlSerializer, SafeXmlSerializer>(); |
The following methods on the serializer classes might be of interest when customizing the serialization:
...
...