From 0faf20f75c15810cdb9c00f8f1ba3ed68d946602 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Thu, 1 Aug 2024 17:00:00 +0200 Subject: [PATCH] add step-by-step descriptions --- .../migration-to-data-contract-serializer.md | 17 ++++++-- .../migration-to-protobuf-net.md | 40 ++++++++++++++++++- 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/docs/standard/serialization/binaryformatter-migration-guide/migration-to-data-contract-serializer.md b/docs/standard/serialization/binaryformatter-migration-guide/migration-to-data-contract-serializer.md index 32e9654deb153..3b6ee3e02a609 100644 --- a/docs/standard/serialization/binaryformatter-migration-guide/migration-to-data-contract-serializer.md +++ b/docs/standard/serialization/binaryformatter-migration-guide/migration-to-data-contract-serializer.md @@ -17,6 +17,19 @@ helpviewer_keywords: The .NET base class libraries provide two XML serializers: [XmlSerializer](../introducing-xml-serialization.md) and [DataContractSerializer](../../../fundamentals/runtime-libraries/system-runtime-serialization-datacontractserializer.md). There are some subtle differences between these two, but for the purpose of the migration, this section focuses only on `DataContractSerializer`. Why? Because it **fully supports the serialization programming model that was used by `BinaryFormatter`**. All the types that are already marked as `[Serializable]` or implement `ISerializable` can be serialized with `DataContractSerializer`. Where is the catch? Known types must be specified up front (that's why it's secure). You need to know them and be able to get the `Type`, **even for private types**. + +It's not required to specify most popular collections or primitive types like `string` or `DateTime` (the serializer has its own default allowlist), but there are exceptions like `DateTimeOffset`. For more information about the supported types, see [Types supported by the data contract serializer](../../../framework/wcf/feature-details/types-supported-by-the-data-contract-serializer.md). + +[Partial trust](../../../framework/wcf/feature-details/partial-trust.md) is a .NET Framework feature that wasn't ported to .NET (Core). If your code runs on .NET Framework and uses this feature, read about the [limitations](../../../framework/wcf/feature-details/types-supported-by-the-data-contract-serializer.md#limitations-of-using-certain-types-in-partial-trust-mode) that might apply to such a scenario. + +## Step by step migration + +1. Find all the usages of `BinaryFormatter`. +2. Ensure that the serialization code paths are covered with tests, so you can verify your changes and avoid introducing bugs. +3. You don't need to install any pacakges, as `DataContractSerializer` is part of the .NET shared framework. +4. Find all the types that are being serialized with `BinaryFormatter`. You don't need to modify any of them, but you may need to list them via `knownTypes` argument of the `DataContractSerializer` ctor. +5. Replace the usage of `BinaryFormatter` with `DataContractSerializer`. + ```csharp DataContractSerializer serializer = new( type: input.GetType(), @@ -26,7 +39,3 @@ DataContractSerializer serializer = new( typeof(MyType2) }); ``` - -It's not required to specify most popular collections or primitive types like `string` or `DateTime` (the serializer has its own default allowlist), but there are exceptions like `DateTimeOffset`. For more information about the supported types, see [Types supported by the data contract serializer](../../../framework/wcf/feature-details/types-supported-by-the-data-contract-serializer.md). - -[Partial trust](../../../framework/wcf/feature-details/partial-trust.md) is a .NET Framework feature that wasn't ported to .NET (Core). If your code runs on .NET Framework and uses this feature, read about the [limitations](../../../framework/wcf/feature-details/types-supported-by-the-data-contract-serializer.md#limitations-of-using-certain-types-in-partial-trust-mode) that might apply to such a scenario. diff --git a/docs/standard/serialization/binaryformatter-migration-guide/migration-to-protobuf-net.md b/docs/standard/serialization/binaryformatter-migration-guide/migration-to-protobuf-net.md index a460593d5af38..fe0227ccc4d57 100644 --- a/docs/standard/serialization/binaryformatter-migration-guide/migration-to-protobuf-net.md +++ b/docs/standard/serialization/binaryformatter-migration-guide/migration-to-protobuf-net.md @@ -16,6 +16,44 @@ helpviewer_keywords: - By default, both public and non-public types are serializable. Every type needs to provide a parameterless constructor. - protobuf-net requires each serializable type to be annotated with `[ProtoContract]` attribute. -- Every serializable non-static field and a property needs to be annotated with `[ProtoMember(int tag)]` attribute. The member names aren't encoded in the data. Instead, the users must pick an integer to identify each member. +- 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 an integer to identify each member. - [Inheritance](https://github.com/protobuf-net/protobuf-net?tab=readme-ov-file#inheritance) must be explicitly declared via `[ProtoInclude(...)]` attribute on each type with known subtypes. - Read-only fields are supported by default. + +## Step by step migration + +1. Find all the usages of `BinaryFormatter`. +2. Ensure that the serialization code paths are covered with tests, so you can verify your changes and avoid introducing bugs. +3. Install `protobuf-net` package. +4. Find all the types that are being serialized with `BinaryFormatter`. +5. For types that you can modify: + - Annotate with `[ProtoContract]` attribute all types that are marked with `[Serializable]` or implement the `ISerializable` interface. If these types are not exposed to other apps (example: you are writing a library) that may use different serializers like `DataContractSerializer`, you can remove the `[Serializable]` and `ISerializable` 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. 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. + +```diff +-[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; } +} +``` + +6. 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. +7. Replace the usage of `BinaryFormatter` with `ProtoBuf.Serializer`.