CustomSerializerSurrogateSelector Class

Note: This API is now obsolete.
An ISurrogateSelector implementation that makes possible to serialize and deserialize any objects, including non-serializable ones by an IFormatter such as BinarySerializationFormatter or the legacy BinaryFormatter.
When targeting .NET 8 or later this class is marked as obsolete.

See the online help for a more detailed description with examples.

Definition

Namespace: KGySoft.Serialization.Binary
Assembly: KGySoft.CoreLibraries (in KGySoft.CoreLibraries.dll) Version: 9.0.0
C#
[ObsoleteAttribute("It is not recommended to use this class because it's basically used for solving BinaryFormatter incompatibility issues between different platforms. For KGy SOFT's BinarySerializationFormatter this class is not really needed. Or if so, it's a sign that binary serialization is not used in a secure way.")]
public sealed class CustomSerializerSurrogateSelector : ISurrogateSelector, 
	ISerializationSurrogate, IDisposable
Inheritance
Object    CustomSerializerSurrogateSelector
Implements
IDisposable, ISerializationSurrogate, ISurrogateSelector

Remarks

  Security Note

Do not use the CustomSerializerSurrogateSelector class to be able to deserialize any type from an untrusted source. If you deserialize a stream from an untrusted source make sure that you set the SafeMode property, which prevents supporting non-serializable types, or set the IsTypeSupported property to explicitly tell what types are supported.

See also the security notes at the Remarks section of the BinarySerializationFormatter class for more details.

By using the CustomSerializerSurrogateSelector you can serialize and deserialize any types.

Examples

To serialize a non-serializable type, or a type, which contains non-serializable types, it is usually enough to assign a CustomSerializerSurrogateSelector to the formatter:

C#
var formatter = new BinaryFormatter { SurrogateSelector = new CustomSerializerSurrogateSelector() };
formatter.Serialize(stream, myObject);

Solving compatibility issues between different platforms

  Note

Some types that are serializable in .NET Framework are not serializable in .NET Core/.NET Standard. This class can be a solution also for this problem. However, the solution is not always as simple as assigning a CustomSerializerSurrogateSelector instance to the SurrogateSelector property. See some cases and their solution in the list below.

Basic case
If the only difference is that the type, which is serializable in the original platform is not marked by the SerializableAttribute in the new one (e.g.: MemoryStream is not serializable in .NET Core, but otherwise it is still the same as in .NET Framework), then it is enough to use the default CustomSerializerSurrogateSelector:
C#
// deserializing a MemoryStream in .NET Core that was serialized in .NET Framework
var formatter = new BinaryFormatter { SurrogateSelector = new CustomSerializerSurrogateSelector() };
var memoryStream = (MemoryStream)formatter.Deserialize(streamSerializedInNetFramework);
The original type implements ISerializable
In .NET Core there are several types that still implement ISerializable, though their ISerializable.GetObjectData method throws a PlatformNotSupportedException (e.g. DBNull, Type and Assembly in .NET Core 2.0). On the other hand, in .NET Core 3.0 DBNull is serializable again, Assembly remained the same and from Type the ISerializable implementation has also been removed along with the SerializableAttribute. For such cases there are more possible solutions:
  • In best cases it is enough to use the BinarySerializationFormatter both for serializing and deserializing without any surrogate selector. It natively supports Type instances on all platforms.
  • When serializing by CustomSerializerSurrogateSelector you can set the IgnoreISerializable property to force serializing the object by fields. If the fields are the same on the deserializing side, then the object will be deserializable by using the CustomSerializerSurrogateSelector on both sides with the same settings.
  • If fields have also changed you can customize the serializing and deserializing process by handling the events (see below).
Providing backwards compatibility
The Basic case might not work the other way around: serializing an object in .NET Core might not be deserializable in .NET Framework, for example. It can be avoided if:
Deserializing refactored types
If the object was serialized by fields but the field names have been refactored since then, then you can handle the SettingField event where you can either look up and set the appropriate Field or do whatever custom processing and set the Handled property to to indicate that you processed the entry manually. If you can initialize the complete object by yourself based on the serialized SerializationInfo, then you can handle the Deserializing event and set the Handled property to .
C#
// deserializing a type, whose fields used to have "m_" prefix, which have been removed
var surrogate = new CustomSerializerSurrogateSelector();
surrogate.Deserializing += (sender, args) =>
{
    // manipulating data only if the current class is the one that changed
    if (!(args.Object is MyChangedClass))
        return;

    // Replacing serialization entry names using the ReplaceValue extension method.
    // An alternative solution would be initializing args.Object by ourselves and
    // setting the args.Handled to true to prevent default deserialization logic.
    foreach (SerializationEntry entry in args.SerializationInfo)
    {
        if (entry.Name.StartsWith("m_"))
            args.SerializationInfo.ReplaceValue(entry.Name, 
                entry.Name.Substring(2), entry.Value, entry.ObjectType);
    }
};

var formatter = new BinaryFormatter // or a BinarySerializationFormatter
{
    SurrogateSelector = surrogate, // to remap field names as specified above
    Binder = new WeakAssemblySerializationBinder() // if assembly version changed, too
};

return (MyChangedClass)formatter.Deserialize(streamContainingOldData);

  Caution

Some of the solutions above are more workarounds for situations arose rather than recommended practices. If it is known that a type will be deserialized in another environment and it can be completely restored by its public members, then a text-based serialization (see also XmlSerializer) can be a better choice.

Constructors

CustomSerializerSurrogateSelectorInitializes a new instance of the CustomSerializerSurrogateSelector class

Properties

IgnoreISerializable Gets or sets whether the ISerializable implementation should be ignored. The value of this property can be overridden by handling the Serializing event.
Default value: .
IgnoreNonExistingFields Gets or sets whether serialization data without a corresponding field should be silently ignored on deserialization. The field setting can be adjusted by handling the SettingField event.
Default value: .
IgnoreNonSerializedAttribute Gets or sets whether the fields that are marked by NonSerializedAttribute should be forcibly serialized. The value of this property can be overridden by handling the GettingField event.
Default value: .
IsTypeSupported Gets or sets a delegate that can tell whether this CustomSerializerSurrogateSelector instance can be used to serialize and deserialize a type. If this property is , and SafeMode is , then only serializable types are supported. Primitive types, String, arrays, pointers any by-ref types are not supported, regardless of the SafeMode property.
Default value: .
SafeMode Gets or sets whether it is prohibited to serialize and deserialize types that are not marked by SerializableAttribute if the IsTypeSupported property is not set.
Default value: .

Methods

ChainSelector Specifies the next ISurrogateSelector for surrogates to examine if the current instance does not have a surrogate for the specified type and assembly in the specified context.
Dispose Releases the resources held by this CustomSerializerSurrogateSelector instance.
GetNextSelector Returns the next surrogate selector in the chain.
GetSurrogate Finds the surrogate that represents the specified object's type, starting with the specified surrogate selector for the specified serialization context.

Events

Deserializing Occurs when an object is being deserialized.
If you initialize the Object manually make sure you set the Handled property to to omit the default deserialization logic.
GettingField Occurs when a field value is about to be retrieved on serialization. You can adjust the Name, Value and Type of the entry to be stored, or set the Handled property to to prevent storing any value for the current Field. The Handled property might be initialized to for fields that are marked by the NonSerializedAttribute.
ObjectDataObtained Occurs when the SerializationInfo of the object to be serialized has been obtained. You still can adjust its content before the actual serialization.
ObjectDataRestored Occurs when the SerializationInfo of the object to be deserialized has been processed.
Serializing Occurs when an object is being serialized, before saving its inner content.
If you populate the SerializationInfo manually make sure you set the Handled property to to omit the default serialization logic.
SettingField Occurs when a field value is about to be set on deserialization. You can adjust the associated Field and its desired Value to be set or you can set the Handled property to to prevent setting any field by the default logic.

Extension Methods

Convert Converts an Object specified in the obj parameter to the desired targetType.
See the Examples section of the generic ConvertTTarget(Object, CultureInfo) overload for an example.
(Defined by ObjectExtensions)
ConvertTTarget Converts an Object specified in the obj parameter to the desired TTarget.
(Defined by ObjectExtensions)
In Gets whether item is among the elements of set.
See the Examples section of the generic InT(T, T) overload for an example.
(Defined by ObjectExtensions)
TryConvert Tries to convert an Object specified in the obj parameter to the desired targetType.
See the Examples section of the ConvertTTarget(Object, CultureInfo) method for a related example.
(Defined by ObjectExtensions)
TryConvert Tries to convert an Object specified in the obj parameter to the desired targetType.
See the Examples section of the ConvertTTarget(Object, CultureInfo) method for a related example.
(Defined by ObjectExtensions)
TryConvertTTarget Tries to convert an Object specified in the obj parameter to the desired TTarget.
See the Examples section of the ConvertTTarget(Object, CultureInfo) method for a related example.
(Defined by ObjectExtensions)
TryConvertTTarget Tries to convert an Object specified in the obj parameter to the desired TTarget.
See the Examples section of the ConvertTTarget(Object, CultureInfo) method for a related example.
(Defined by ObjectExtensions)

See Also