CustomSerializerSurrogateSelector Class
Note: This API is now obsolete.
Namespace: KGySoft.Serialization.BinaryAssembly: KGySoft.CoreLibraries (in KGySoft.CoreLibraries.dll) Version: 9.0.0
[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
<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 NotInheritable Class CustomSerializerSurrogateSelector
Implements ISurrogateSelector, ISerializationSurrogate, IDisposable
[ObsoleteAttribute(L"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 ref class CustomSerializerSurrogateSelector sealed : ISurrogateSelector,
ISerializationSurrogate, IDisposable
[<SealedAttribute>]
[<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.")>]
type CustomSerializerSurrogateSelector =
class
interface ISurrogateSelector
interface ISerializationSurrogate
interface IDisposable
end
- Inheritance
- Object CustomSerializerSurrogateSelector
- Implements
- IDisposable, ISerializationSurrogate, ISurrogateSelector
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.
The
BinarySerializationFormatter is also able to serialize non-serializable types by itself
by using the
RecursiveSerializationAsFallback option. But the
CustomSerializerSurrogateSelector provides more control and can be used also for other formatters.
Please note though that you cannot use this class for
BinarySerializationFormatter when the
SafeMode flag
is enabled in its
Options property, even if the
SafeMode property is set to
.
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:
var formatter = new BinaryFormatter { SurrogateSelector = new CustomSerializerSurrogateSelector() };
formatter.Serialize(stream, myObject);
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:
// 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 .
// 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);
If the name of the type changed too, you can use the
CustomSerializationBinder class.
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.
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: .
|
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.
|
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.
|