DouglasDwyer.Dozer
0.1.0
See the version list below for details.
dotnet add package DouglasDwyer.Dozer --version 0.1.0
NuGet\Install-Package DouglasDwyer.Dozer -Version 0.1.0
<PackageReference Include="DouglasDwyer.Dozer" Version="0.1.0" />
<PackageVersion Include="DouglasDwyer.Dozer" Version="0.1.0" />
<PackageReference Include="DouglasDwyer.Dozer" />
paket add DouglasDwyer.Dozer --version 0.1.0
#r "nuget: DouglasDwyer.Dozer, 0.1.0"
#:package DouglasDwyer.Dozer@0.1.0
#addin nuget:?package=DouglasDwyer.Dozer&version=0.1.0
#tool nuget:?package=DouglasDwyer.Dozer&version=0.1.0
🚜 Dozer
Doug's binary serializer
Dozer is a general-purpose serializer for modern .NET, inspired by Ceras. It converts between C# objects and byte arrays, with support for:
- References and cycles: all object references are preserved, including cyclic references. The entire object graph is represented in the serialized data.
- Polymorphic types: base classes, boxed value types, and interfaces are all serializable. When necessary, Dozer includes
TypeandAssemblydata in the binary output, so that types can be dynamically loaded during deserialization. - Annotationless serialization: by default, Dozer will serialize the public fields and auto properties on a type. No extra attributes are required to make a type serializable.
- Blitting
unmanagedstructs: if typeT's binary format and managed representation are equivalent, then Dozer will copy the bytes ofTandT[]verbatim. This eliminates the overhead of calling serialization methods for individual fields.
Installation
This project is available on Nuget as DouglasDwyer.Dozer:
dotnet add package DouglasDwyer.Dozer
How to use
Dozer is robust but quite simple to use. The following code snippet serializes and then deserializes an object, storing all of the object's data in a byte array and then retrieving it:
var serializer = new DozerSerializer();
var objs = new List<object>() { 73, "hello", false };
var data = serializer.Serialize(objs);
var deserialized = serializer.Deserialize<List<object>>(data);
Console.WriteLine(objs.SequenceEqual(deserialized)); // true
For documentation about all functionality included in Dozer, please see the complete API reference.
Features
Comparison with other popular libraries
| Feature \ Library | MessagePack | Ceras | Dozer |
|---|---|---|---|
| References/cycles | ❌ | ✔️ | ✔️ |
| Polymorphism | 🟡 (requires Union attribute or including the full type name for every serialized object) |
✔️ | ✔️ |
| Annotationless serialization | ❌ | ✔️ | ✔️ |
Blitting unmanaged types |
❌ | 🟡 (unsafe handling of padding, bool, and decimal) |
✔️ |
| Thread-safe | ✔️ | ❌ | ✔️ |
| Standard types | ✔️ | 🟡 (missing types from .NET 6+) | ✔️ |
| AoT support | ✔️ | ✔️ | ❌ |
| Version tolerance | ✔️ | ✔️ | ❌ |
| Last update |
Improvements over MessagePack/Ceras
- Array covariance: Dozer will properly preserve the types of covariant arrays: for example, a
string[]serialized as typeobject[]will be deserialized with underlying typestring[]. Using Ceras, the deserialized type would beobject[]. - Additional
Systemtypes: Dozer has builtin support forArraySegment<T>,CultureInfo,Memory<T>, andReadOnlyMemory<T>. - Collection comparers: Dozer will properly preserve the
IComparers andIEqualityComparers for common collections (such asDictionary<K, V>,HashSet<T>orImmutableSortedSet<T>). Ceras does not include the comparer during serialization. - Input interface: for reading binary input, Dozer accepts a
ReadOnlySpan<byte>. This allows for safely accepting input from unmanaged sources. In contrast, MessagePack requires aReadOnlyMemory<byte>and Ceras requires abyte[], both of which are more restrictive. - Known types/assemblies: to reduce binary size when encoding type information, Ceras allows the user to specify a
KnownTypeslist. Ceras associates the list order with integer IDs for each type - therefore, the list's order cannot be changed later. In contrast, Dozer accepts a set ofKnownAssembliesfrom which it generates eight-byte type ID hashes. Adding known assemblies is much easier - it is not required to list every single type. This approach still works if assemblies are added, removed, or reordered, making it suitable even for dynamically-loaded assemblies (like plugins). - Output interface: for writing binary output, Dozer accepts an
IBufferWrite<byte>. In contrast, Ceras requires abyte[], which is more restrictive. - Safe handling of blittable types: Ceras will blit types containing padding (such as
struct Foo { byte A; int B; }), which can expose the contents of uninitialized memory. Ceras will also blit types containingboolanddecimalwithout validating that the bit patterns are valid (for instance,boolshould only be0or1). Dozer checks for these cases - it will only blit structs when there is no padding and any bit pattern is valid.
Thread-safety
All of Dozer's methods are completely thread-safe, and multiple objects may be serialized/deserialized using the same serializer instance at the same time. However, modifying an object that is currently being serialized may lead to unexpected results. While serialization will complete successfully, different parts of the object may be written to the output at different times. This means that a modification during serialization could lead to the serialization data coding for an object state that never actually existed in-memory. For example, consider the following:
- An
(int, string)tuple of value(0, "")is passed intoDozerSerializer.Serialize(). The initial serialization data contains the value(?, ?), because neither field has been written yet. - Another thread increments the
intfield, changing the state of the object to(1, ""). - The serializer serializes the
intfield, and the serialization data is now(1, ?). - The other thread decrements the
intfield, and then sets thestringto "hello", changing the state of the object to(0, "hello"). - The serializer serializes the
stringfield, resulting in a serialized object of(1, "hello").
Though this situation is exceedingly rare, users should be wary of modifying their objects during serialization. Concurrent modifications can result in a serialized object whose state never existed in-memory; (1, "hello") was the result of the above example's serialization, but the object's value was never (1, "hello").
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net9.0
- CommunityToolkit.HighPerformance (>= 8.4.0)
- FastExpressionCompiler (>= 5.3.0)
- Microsoft.Extensions.ObjectPool (>= 9.0.10)
- System.Collections.Immutable (>= 5.0.0)
- System.IO.Hashing (>= 9.0.10)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.