Skip to content

Commit

Permalink
add step-by-step descriptions
Browse files Browse the repository at this point in the history
  • Loading branch information
adamsitnik committed Aug 1, 2024
1 parent 4e76345 commit 0faf20f
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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**.


Check failure on line 20 in docs/standard/serialization/binaryformatter-migration-guide/migration-to-data-contract-serializer.md

View workflow job for this annotation

GitHub Actions / lint

Multiple consecutive blank lines [Expected: 1; Actual: 2]
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(),
Expand All @@ -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.
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Check failure on line 54 in docs/standard/serialization/binaryformatter-migration-guide/migration-to-protobuf-net.md

View workflow job for this annotation

GitHub Actions / lint

Ordered list item prefix [Expected: 1; Actual: 6; Style: 1/2/3]
- 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`.

Check failure on line 59 in docs/standard/serialization/binaryformatter-migration-guide/migration-to-protobuf-net.md

View workflow job for this annotation

GitHub Actions / lint

Ordered list item prefix [Expected: 2; Actual: 7; Style: 1/2/3]

0 comments on commit 0faf20f

Please sign in to comment.