Migrate to protobuf-net (binary)
The protobuf-net library is a contract-based serializer for .NET that uses the binary Protocol Buffers serialization format. The API follows typical .NET patterns and is broadly comparable to XmlSerializer
and DataContractSerializer
.
Some behaviors and features of protobuf-net will be notable during migrations from BinaryFormatter, and many scenarios require applying attributes to members.
- By default, both public and non-public types are serializable, with the serializer expecting a parameterless constructor.
- protobuf-net requires each serializable type to be annotated with
[ProtoContract]
attribute; this attribute can optionally specify theSkipConstructor = true
property, which removes the need for any particular constructor. - Every serializable non-static field and a property needs to be annotated with
[ProtoMember(int identifier)]
attribute. The member names aren't encoded in the data. Instead, the users must pick a positive integer to identify each member which must be unique within that type. - Inheritance must be explicitly declared via
[ProtoInclude(...)]
attribute on each type with known subtypes. - Read-only fields are supported by default.
- Alternatively, some non-attributed tuple-like types are recognized by constructor pattern; a type with a constructor that has parameters that match (by name) all of the declared public members will be interpreted as a tuple, and the parameter order will be used to infer the identifier for that member.
- The use of the
protobuf-net.BuildTools
design-time package is highly recommended; this offers compile-time warnings of common errors.
Step by step migration
- Find all the usages of
BinaryFormatter
. - Ensure that the serialization code paths are covered with tests, so you can verify your changes and avoid introducing bugs.
- Install
protobuf-net
package (and optionallyprotobuf-net.BuildTools
). - Find all the types that are being serialized with
BinaryFormatter
. - For types that you can modify:
- Annotate with
[ProtoContract]
attribute all types that are marked with[Serializable]
or implement theISerializable
interface. If these types are not exposed to other apps (example: you are writing a library) that may use different serializers likeDataContractSerializer
, you can remove the[Serializable]
andISerializable
annotations. - For derived types, apply
[ProtoInclude(...)]
to their base types (see the example below). - For every type that declares any constructor that accepts parameters, add a parameterless constructor, or specify
SkipConstructor = true
on the[ProtoContract]
attribute. Leave a comment that explains the protobuf-net requirement (so nobody removes it by accident). - Mark all the members (fields and properties) that you wish to serialize with
[ProtoMember(int identifier)]
. All identifiers must be unique within a single type, but the same numbers can be re-used in sub-types if inheritance is enabled.
- Annotate with
- For types that you can't modify:
- For types provided by the .NET itself, you can use
ProtoBuf.Meta.RuntimeTypeModel.Default.CanSerialize(Type type)
API to check if they are natively supported by protobuf-net. - You can create dedicated data transfer objects (DTO) and map them accordingly (you could use implicit cast operator for that).
- Use the
RuntimeTypeModel
API to define everything that the attributes allow for.
- For types provided by the .NET itself, you can use
- Replace the usage of
BinaryFormatter
withProtoBuf.Serializer
.
-[Serializable]
+[ProtoContract]
+[ProtoInclude(2, typeof(Point2D))]
public class Point1D
{
+ [ProtoMember(1)]
public int X { get; set; }
}
-[Serializable]
+[ProtoContract]
public class Point2D : Point1D
{
+ [ProtoMember(2)]
public int Y { get; set; }
}
Collaborate with us on GitHub
The source for this content can be found on GitHub, where you can also create and review issues and pull requests. For more information, see our contributor guide.