See the Remarks section for details and for the differences to BinaryFormatter.
KGySoft.Serialization.BinaryBinarySerializationFormatter
Namespace: KGySoft.Serialization.Binary
Assembly: KGySoft.CoreLibraries (in KGySoft.CoreLibraries.dll) Version: 7.0.0-preview.3
The BinarySerializationFormatter type exposes the following members.
Name | Description | |
---|---|---|
![]() | BinarySerializationFormatter |
Creates a new instance of BinarySerializationFormatter class.
|
Name | Description | |
---|---|---|
![]() | Binder |
Gets or sets the SerializationBinder that performs type conversions to and from string.
See the Remarks section for details. |
![]() | Context |
Gets or sets the StreamingContext used for serialization and deserialization.
|
![]() | Options |
Options used for serialization and deserialization.
See the BinarySerializationOptions enumeration for details. |
![]() | SurrogateSelector |
Gets or sets an ISurrogateSelector can be used to customize serialization and deserialization.
|
Name | Description | |
---|---|---|
![]() | Deserialize |
Deserializes the specified part of a byte array into an object.
|
![]() | DeserializeByReader |
Deserializes data beginning at current position of given reader.
|
![]() | DeserializeFromStream |
Deserializes data beginning at current position of given stream.
|
![]() | Serialize |
Serializes an object into a byte array.
|
![]() | SerializeByWriter |
Serializes the given data by using the provided writer.
|
![]() | SerializeToStream |
Serializes the given data into a stream.
|
Name | Description | |
---|---|---|
![]() | Convert(Type, CultureInfo) | Overloaded.
Converts an Object specified in the obj parameter to the desired targetType.
(Defined by ObjectExtensions.)See the Examples section of the generic ConvertTTarget(Object, CultureInfo) overload for an example. |
![]() ![]() | ConvertTTarget(CultureInfo) | Overloaded.
Converts an Object specified in the obj parameter to the desired TTarget.
(Defined by ObjectExtensions.)See the Remarks section for details. |
![]() | In |
Gets whether item is among the elements of set.
(Defined by ObjectExtensions.)See the Examples section of the generic InT(T, T) overload for an example. |
![]() | TryConvert(Type, Object) | Overloaded.
Tries to convert an Object specified in the obj parameter to the desired targetType.
(Defined by ObjectExtensions.)See the Examples section of the ConvertTTarget(Object, CultureInfo) method for a related example. |
![]() | TryConvert(Type, CultureInfo, Object) | Overloaded.
Tries to convert an Object specified in the obj parameter to the desired targetType.
(Defined by ObjectExtensions.)See the Examples section of the ConvertTTarget(Object, CultureInfo) method for a related example. |
![]() | TryConvertTTarget(TTarget) | Overloaded.
Tries to convert an Object specified in the obj parameter to the desired TTarget.
(Defined by ObjectExtensions.)See the Examples section of the ConvertTTarget(Object, CultureInfo) method for a related example. |
![]() | TryConvertTTarget(CultureInfo, TTarget) | Overloaded.
Tries to convert an Object specified in the obj parameter to the desired TTarget.
(Defined by ObjectExtensions.)See the Examples section of the ConvertTTarget(Object, CultureInfo) method for a related example. |
![]() |
---|
The fundamental goal of binary serialization is to store the bitwise content of an object, hence in general case it relies on field values (including private ones), which can change from version to version. Therefore, binary serialization is recommended only for in-process purposes, such as deep cloning or undo/redo, etc. 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. |
![]() |
---|
Do not use binary serialization if the serialization stream may come from an untrusted source (eg. remote service, file or database). If you still need to do so (eg. due to compatibility), then it is highly recommended to enable the SafeMode option, which prevents loading assemblies during the deserialization as well as instantiating non-serializable types, and guards against some attacks that may cause OutOfMemoryException. When using SafeMode you must preload every assembly referred by the serialization stream. Please note though that even some system types can be dangerous. In the .NET Framework there are some serializable types in the fundamental core assemblies that can be exploited for several attacks (causing unresponsiveness, StackOverflowException or even files to be deleted). Starting with .NET Core these types are not serializable anymore and some of them have been moved to separate NuGet packages anyway, but the BinaryFormatter in the .NET Framework is still vulnerable against such attacks. When using the SafeMode flag, the BinarySerializationFormatter is protected against the known security issues on all platforms but of course it cannot guard you against the already loaded potentially harmful types. Please also note that SafeMode cannot prevent deserializing invalid content if a serializable type does not implement ISerializable and does it not validate the incoming SerializationInfo in its serialization constructor. All serializable types that can have an invalid state regarding the field values should implement ISerializable and should throw a SerializationException from their serialization constructor if validation fails. Other exceptions thrown by the constructor will be wrapped into a SerializationException. To be completely secured use binary serialization in-process only, or (especially when targeting the .NET Framework), set the Binder property to a SerializationBinder instance that uses strict mapping. For example, you can use the CustomSerializationBinder class with handlers that throw exceptions for unexpected assemblies and types. Please also note that if the Binder property is set, then using SafeMode cannot prevent loading assemblies by the binder itself. It can just assure that if the binder returns , then the default resolve logic will not allow loading assemblies. The binders in this library that can perform automatic type resolving, such the WeakAssemblySerializationBinder and ForwardedTypesSerializationBinder have their own SafeMode property. If you use them, make sure to set their SafeMode property to to prevent loading assemblies by the binders themselves. Similarly, if the SurrogateSelector property is set, then they provide a custom serialization even for types that are not serializable. The surrogate selectors in this library, such as the CustomSerializerSurrogateSelector and NameInvariantSurrogateSelector types have their own SafeMode property. If you use them, make sure to set their SafeMode property to to prevent deserializing non-serializable types. |
BinarySerializationFormatter aims to serialize objects effectively where the serialized data is almost always more compact than the results produced by the BinaryFormatter class.
BinarySerializationFormatter natively supports all of the primitive types and a sort of other simple types, arrays, generic and non-generic collections.
![]() |
---|
Serialization of natively supported types produce an especially compact result because these types are not serialized by traversing and storing the fields of the object graph recursively. This means not just better performance for these types but also prevents compatibility issues between different platforms because these types are not encoded by assembly identity and type name. Serialization of complex types can be somewhat slower for the first time than by BinaryFormatter but the serialized result is almost always shorter than the one by BinaryFormatter, especially when generic types are involved. |
Even if a type is not marked to be serializable by the SerializableAttribute, then you can use the RecursiveSerializationAsFallback option to force their serialization. Alternatively, you can implement the IBinarySerializable interface, which can be used to produce a more compact custom serialization than the one provided by implementing the ISerializable interface. A custom serialization logic can be applied also by setting the SurrogateSelector property.
Similarly to BinaryFormatter, ISerializable implementations are also supported, and they are considered only for types marked by the SerializableAttribute, unless the RecursiveSerializationAsFallback option is enabled for the serialization.As BinarySerializationFormatter implements IFormatter it fully supports SerializationBinder and ISurrogateSelector implementations.
![]() |
---|
A SerializationBinder can be used to deserialize types of unmatching assembly identity and to specify custom type-name mappings in both directions. Though BinarySerializationFormatter automatically handles TypeForwardedToAttribute and TypeForwardedFromAttribute (see also the IgnoreTypeForwardedFromAttribute option), you can use also the ForwardedTypesSerializationBinder, especially for types without a defined forwarding. The WeakAssemblySerializationBinder can also be general solution if you need to ignore the assembly version or the complete assembly identity on resolving a type. If the name of the type has also been changed, then the CustomSerializationBinder can be used. See also the Remarks section of the Binder property for more details. |
![]() |
---|
An ISurrogateSelector can be used to customize serialization and deserialization. It can be used for types that cannot be handled anyway for some reason. For example, if you need to deserialize types, whose field names have been renamed you can use the CustomSerializerSurrogateSelector. Or, if the produced raw data has to be compatible with the obfuscated version of a type, then it can be achieved by the NameInvariantSurrogateSelector. |
There are three ways to serialize/deserialize an object. To serialize into a byte array use the Serialize method. Its result can be deserialized by the Deserialize method. Additionally, you can use the SerializeToStream/DeserializeFromStream methods to dump/read the result to and from a Stream, and the the SerializeByWriter/DeserializeByReader methods to use specific BinaryWriter and BinaryReader instances for serialization and deserialization, respectively.
![]() |
---|
In .NET Framework almost every type was serializable by BinaryFormatter. In .NET Core this principle has been radically changed. Many types are just simply not marked by the SerializableAttribute anymore (eg. MemoryStream, CultureInfo, Encoding), and also there are some others, which still implement ISerializable but their GetObjectData throw a PlatformNotSupportedException now. Binary serialization of these types is not recommended anymore. If you still must serialize or deserialize such types see the Remarks section of the CustomSerializerSurrogateSelector for more details. |
Natively supported simple types
Following types are natively supported. When these types are serialized, no recursive traversal of the fields occurs:
- reference
- Non-derived Object instances.
- DBNull
- Boolean
- SByte
- Byte
- Int16
- UInt16
- Int32
- UInt32
- Int64
- UInt64
- Char
- String
- Single
- Double
- Decimal
- DateTime
- TimeSpan
- DateTimeOffset
- IntPtr
- UIntPtr
- Version
- Guid
- Uri
- StringBuilder
- BigInteger (in .NET Framework 4.0 and above)
- Rune (in .NET Core 3.0 and above)
- Index (in .NET Standard 2.1 and above)
- Range (in .NET Standard 2.1 and above)
- Half (in .NET 5.0 and above)
- DateOnly (in .NET 6.0 and above)
- TimeOnly (in .NET 6.0 and above)
- Int128 (in .NET 7.0 and above)
- UInt128 (in .NET 7.0 and above)
- Enum types
- Type instances if they are runtime types.
- NullableT types if type parameter is any of the supported types.
- Any object that implements the IBinarySerializable interface.
- KeyValuePairTKey, TValue if Key and Value are any of the supported types.
- DictionaryEntry if Key and Value are any of the supported types.
![]() |
---|
|
Natively supported generic collections
Following generic collections are natively supported. When their generic arguments are one of the simple types or other supported collections, then no recursive traversal of the fields occurs:
- Array of element types above or compound of other supported collections
- ListT
- CircularListT
- LinkedListT
- HashSetT
- QueueT
- StackT
- SortedSetT (in .NET Framework 4.0 and above)
- ConcurrentBagT (in .NET Framework 4.0 and above)
- ConcurrentQueueT (in .NET Framework 4.0 and above)
- ConcurrentStackT (in .NET Framework 4.0 and above)
- DictionaryTKey, TValue
- SortedListTKey, TValue
- SortedDictionaryTKey, TValue
- CircularSortedListTKey, TValue
- ConcurrentDictionaryTKey, TValue (in .NET Framework 4.0 and above)
![]() |
---|
|
![]() |
---|
The shortest result can be achieved by using classes or value types as array base types and generic parameters. |
Natively supported non-generic collections
Following non-generic collections are natively supported. When they contain only other natively supported elements, then no recursive traversal of the fields occurs:
Collection type | Used element type |
---|---|
ArrayList | Object |
Queue | Object |
Stack | Object |
StringCollection | String |
Hashtable | Object |
SortedList | Object |
ListDictionary | Object |
HybridDictionary | Object |
OrderedDictionary | Object |
StringDictionary | String |
BitArray | Boolean (actually the type is stored in a compact way) |
BitVector32 | Boolean (actually the type is stored in a compact way) |
BitVector32Section | n.a. |
![]() |
---|
|
Serialization events
BinarySerializationFormatter supports calling methods decorated by OnSerializingAttribute, OnSerializedAttribute, OnDeserializingAttribute and OnDeserializedAttribute as well as calling IDeserializationCallback.OnDeserialization method. Attributes should be used on methods that have a single StreamingContext parameter.
![]() |
---|
Please note that if a value type was serialized by the CompactSerializationOfStructures option, then the method of OnDeserializingAttribute can be invoked only after restoring the whole content so fields will be already restored. |
![]() |
---|
Try also online. |
using System; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; using KGySoft.CoreLibraries; using KGySoft.Serialization.Binary; public static class Example { public static void Main() { IFormatter formatter; // feel free to change the type in NextObject<> var instance = ThreadSafeRandom.Instance.NextObject<Dictionary<int, List<string>>>(); Console.WriteLine("Generated object: " + Dump(instance)); using (var ms = new MemoryStream()) { // serializing by KGy SOFT version: formatter = new BinarySerializationFormatter(); formatter.Serialize(ms, instance); // deserialization: ms.Position = 0L; object deserialized = formatter.Deserialize(ms); Console.WriteLine("Deserialized object " + Dump(deserialized)); Console.WriteLine("Length by BinarySerializationFormatter: " + ms.Length); } using (var ms = new MemoryStream()) { // serializing by System version: formatter = new BinaryFormatter(); formatter.Serialize(ms, instance); Console.WriteLine("Length by BinaryFormatter: " + ms.Length); } } private static string Dump(object o) { if (o == null) return "<null>"; if (o is IConvertible convertible) return convertible.ToString(CultureInfo.InvariantCulture); if (o is IEnumerable enumerable) return $"[{enumerable.Cast<object>().Select(Dump).Join(", ")}]"; return $"{{{o.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).Select(p => $"{p.Name} = {Dump(p.GetValue(o))}").Join(", ")}}}"; } } // This code example produces a similar output to this one: // Generated object: [{Key = 1418272504, Value = [aqez]}, {Key = 552276491, Value = [addejibude, yifefa]}] // Deserialized object [{Key = 1418272504, Value = [aqez]}, {Key = 552276491, Value = [addejibude, yifefa]}] // Length by BinarySerializationFormatter: 50 // Length by BinaryFormatter: 2217