Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BinaryFormatter Migration Guide #41564

Merged
merged 65 commits into from
Aug 8, 2024
Merged
Show file tree
Hide file tree
Changes from 51 commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
e8dd236
Introduce the outline of a BinaryFormatter migration guide
jeffhandley Apr 24, 2024
8465499
Update NRBF Decoder description
adamsitnik Jun 24, 2024
2c86e69
finish the docs for what we are going to ship in Preview 6:
adamsitnik Jun 26, 2024
c8072fb
fix the build warnings
adamsitnik Jun 26, 2024
21eb44d
add missing file extensions to the relative links (they work anyway, …
adamsitnik Jun 26, 2024
b8d5a3e
fix build errors
adamsitnik Jun 26, 2024
805d7b2
Apply suggestions from code review
adamsitnik Jun 27, 2024
5185093
address code review feedback
adamsitnik Jun 27, 2024
66bedc3
add protobuf-net
adamsitnik Jun 27, 2024
a504238
fix the character
adamsitnik Jun 27, 2024
fe0a882
Apply suggestions from code review
adamsitnik Jul 26, 2024
fd2c330
introducde dedicated migration sections for each described serializer
adamsitnik Jul 26, 2024
b0acc01
remove dev_langs from pages where there are no code samples
adamsitnik Jul 26, 2024
c04d901
rename "choosing" -> "choose" and "reading" -> "read"
adamsitnik Jul 31, 2024
f793dcc
initial WinForms guide
lonitra Jul 31, 2024
4e76345
winforms: fix title and lint issues
lonitra Jul 31, 2024
0faf20f
add step-by-step descriptions
adamsitnik Aug 1, 2024
7d8e7a1
fix the build errors
adamsitnik Aug 1, 2024
3f471f3
address feedback
lonitra Aug 1, 2024
2b06beb
fix url
lonitra Aug 1, 2024
484fa6e
address feedback
lonitra Aug 2, 2024
71729aa
Apply suggestions from code review
lonitra Aug 2, 2024
fa00b2f
Merge branch 'main' into binaryformatter-migration-guide
jeffhandley Aug 3, 2024
d3073ec
Rename files to remove 'overview' from URL and dashes from compound n…
jeffhandley Aug 3, 2024
da8c92b
Improve titles/descriptions; rename files to match. Address feedback.…
jeffhandley Aug 3, 2024
eec25a0
Merge branch 'binaryformatter-migration-guide' into migrationStepByStep
jeffhandley Aug 3, 2024
67aae86
Add beginning of step-by-step migration descriptions
jeffhandley Aug 3, 2024
2e3db58
Apply feedback, including rearranging some content. Author more prose…
jeffhandley Aug 3, 2024
1d6991b
Update some wording, formatting, and titles
jeffhandley Aug 3, 2024
00ddf41
Update MessagePack migration docs
AArnott Aug 3, 2024
57019bc
Fix broken links
adamsitnik Aug 5, 2024
f81998b
protobuf-net additions
mgravell Aug 5, 2024
098ec57
Add WPF migration guide
rchauhan18 Aug 5, 2024
2f228e6
address feedback
lonitra Aug 5, 2024
4b5ebff
Reword introduction and introduce [!CAUTION] sections
terrajobst Aug 5, 2024
2b51171
winforms: add loading resources during runtime section
lonitra Aug 5, 2024
8e64322
Merge branch 'binaryformatter-migration-guide' of https://github.com/…
rchauhan18 Aug 6, 2024
24fdc35
Adding short and ushort to primitive list
rchauhan18 Aug 6, 2024
e4dfa48
winforms/wpf: initial clipboard guidance
lonitra Aug 6, 2024
161fecb
winforms/wpf: fix wpf name
lonitra Aug 6, 2024
2a0193b
Link to BinaryFormatter security guide
terrajobst Aug 6, 2024
fe8cfa8
Change warning to caution and link to migration guide
terrajobst Aug 6, 2024
3daa13f
Add an intro to the migration section
terrajobst Aug 6, 2024
06e5575
winforms/wpf: add BinaryFormatter usage in drag/drop
lonitra Aug 6, 2024
7667e96
Apply suggestions from code review
terrajobst Aug 7, 2024
96e6861
Apply suggestions from Genevieve
terrajobst Aug 7, 2024
7118ca7
Link migration of managed resources from index.md
terrajobst Aug 7, 2024
09bd441
Clarify why DCS is easiest to migrate to
terrajobst Aug 7, 2024
4d854c4
Address feedback and take another pass over the content to make impro…
jeffhandley Aug 7, 2024
c874cb2
Merge branch 'binaryformatter-migration-guide' of https://github.com/…
jeffhandley Aug 7, 2024
b129893
Reword SerializationBinder note per suggestion
jeffhandley Aug 7, 2024
2cea254
Address feedback on intro
jeffhandley Aug 7, 2024
0d717e9
Apply feedback to WPF and WinForms pages
jeffhandley Aug 7, 2024
44a4c42
Trim trailing whitespace
jeffhandley Aug 7, 2024
175b87d
Merge branch 'binaryformatter-migration-guide' of https://github.com/…
jeffhandley Aug 7, 2024
7f986d5
Fix lint whitespace errors
jeffhandley Aug 7, 2024
9a3eec6
Apply feedback: "decision on" --> "reason for"
jeffhandley Aug 7, 2024
caf3729
Apply feedback for C# auto properties and "drag-and-drop" usage
jeffhandley Aug 7, 2024
9506364
Fix broken links
jeffhandley Aug 7, 2024
6c55a0a
Add ref to common OLE scenario and update Journaling
rchauhan18 Aug 7, 2024
d1e6060
Apply suggestions from code review
adamsitnik Aug 7, 2024
406050a
Apply suggestions from code review
lonitra Aug 7, 2024
c0ee74a
Blurbs for submitting issues in the 3 repos
jeffhandley Aug 7, 2024
41f2d34
Link to the migration guide from the BinaryFormatter Security Guide
jeffhandley Aug 7, 2024
fc82129
Fix link
terrajobst Aug 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions docs/fundamentals/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -863,6 +863,32 @@ items:
href: ../standard/serialization/xml-schema-definition-tool-xsd-exe.md
- name: Binary serialization
items:
- name: BinaryFormatter migration guide
items:
- name: Overview
href: ../standard/serialization/binaryformatter-migration-guide/index.md
- name: Choose a serializer
href: ../standard/serialization/binaryformatter-migration-guide/choose-a-serializer.md
- name: Migrate to System.Text.Json (JSON)
href: ../standard/serialization/binaryformatter-migration-guide/migrate-to-system-text-json.md
- name: Migrate to DataContractSerializer (XML)
href: ../standard/serialization/binaryformatter-migration-guide/migrate-to-datacontractserializer.md
- name: Migrate to MessagePack (binary)
href: ../standard/serialization/binaryformatter-migration-guide/migrate-to-messagepack.md
- name: Migrate to protobuf-net (binary)
href: ../standard/serialization/binaryformatter-migration-guide/migrate-to-protobuf-net.md
- name: Read BinaryFormatter (NRBF) payloads
href: ../standard/serialization/binaryformatter-migration-guide/read-nrbf-payloads.md
- name: Compatibility package
href: ../standard/serialization/binaryformatter-migration-guide/compatibility-package.md
- name: BinaryFormatter functionality reference
href: ../standard/serialization/binaryformatter-migration-guide/functionality-reference.md
- name: Windows Forms apps
href: ../standard/serialization/binaryformatter-migration-guide/winforms-applications.md
- name: WPF apps
href: ../standard/serialization/binaryformatter-migration-guide/wpf-applications.md
- name: Clipboard and drag-and-drop guidance
href: ../standard/serialization/binaryformatter-migration-guide/winforms-wpf-ole-guidance.md
- name: BinaryFormatter security guide
jeffhandley marked this conversation as resolved.
Show resolved Hide resolved
href: ../standard/serialization/binaryformatter-security-guide.md
- name: BinaryFormatter event source
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
---
title: "BinaryFormatter migration guide: Choose a serializer"
description: "Compare the capabilities and trade-offs of serializers to choose a replacement for BinaryFormatter."
ms.date: 5/31/2024
no-loc: [BinaryFormatter, Serialization]
helpviewer_keywords:
- "BinaryFormatter"
- "serializing objects"
- "serialization"
- "objects, serializing"
---

# Choose a serializer
terrajobst marked this conversation as resolved.
Show resolved Hide resolved

There is no drop-in replacement for BinaryFormatter, but there are several serializers recommended for serializing .NET types. Regardless of which serializer you choose, changes will be needed for integration with the new serializer. During these migrations, it's important to consider the trade-offs between coercing the new serializer to handle existing types with as few changes as possible vs. refactoring types to enable idiomatic serialization with the chosen serializer. Once a serializer is chosen, its documentation should be studied for best practices.

If a binary serialization format is not a requirement, you can consider using JSON or XML serialization formats. These serializers are included in .NET and are officially supported.

1. JSON using [System.Text.Json](./migrate-to-system-text-json.md)
2. XML using [`System.Runtime.Serialization.DataContractSerializer`](./migrate-to-datacontractserializer.md)

If a compact binary representation is important for your scenarios, the following serialization formats and open-source serializers are recommended:

1. [MessagePack](https://msgpack.org/) using [MessagePack for C#](./migrate-to-messagepack.md)
2. [Protocol Buffers](https://protobuf.dev/) using [protobuf-net](./migrate-to-protobuf-net.md)

Whether you have control to change the API shape of the serialized type will influence your direction and approach to serialization. Migration to these serializers may be more straightforward with the ability to annotate types with new attributes, add new constructors, make types/members public, and change fields to properties. Without that ability, using modern serializers might require implementation of custom converters or resolvers.

| Feature | BinaryFormatter | System.Text.Json | DataContractSerializer | MessagePack for C# | protobuf-net |
|------------------------------------------------|------------------|-------------------------|------------------------|--------------------------|-----------------------------------|
| Serialization format | binary (NRBF) | JSON | XML | binary (MessagePack) | binary (Protocol Buffers) |
| Compact representation | ✔️ | ❌ | ❌ | ✔️ | ✔️ |
| Human-readable | ❌️ | ✔️ | ✔️ | ❌️ | ❌️ |
| Performance | ❌️ | ✔️ | ❌ | ✔️ | ✔️ |
| `[Serializable]` attribute support | ✔️ | ❌ | ✔️ | ❌ | ❌ |
| Serializing public types | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
| Serializing non-public types | ✔️ | ✔️ | ✔️ | ✔️ (resolver required) | ✔️ |
| Serializing fields | ✔️ | ✔️ (opt in) | ✔️ | ✔️ (attribute required) | ✔️ (attribute required) |
| Serializing non-public fields | ✔️ | ✔️ (resolver required) | ✔️ | ✔️ (resolver required) | ✔️ (attribute required) |
| Serializing properties | ✔️<sup>*</sup> | ✔️ | ✔️ | ✔️ (attribute required) | ✔️ (attribute required) |
| Deserializing readonly members | ✔️ | ✔️ (attribute required) | ✔️ | ✔️ | ✔️ (parameterless ctor required) |
| Polymorphic type hierarchy | ✔️ | ✔️ (attribute required) | ✔️ | ✔️ (attribute required) | ✔️ (attribute required) |
| AOT support | ❌️ | ✔️ | ❌ | ✔️ | ❌ (planned) |

## JSON using System.Text.Json

The [`System.Text.Json`](../system-text-json/overview.md) library is a modern serializer that emphasizes security, high performance, and low memory allocation for the JavaScript Object Notation (JSON) format. JSON is human-readable and has broad cross-platform support. While text-based format is not as compact as binary formats, it can be significantly reduced in size through compression.

Serialization excludes non-public and readonly members unless specifically handled through attributes and constructors. System.Text.Json also supports [custom serialization and deserialization](../system-text-json/custom-contracts.md) for more control over how types are converted into JSON and vice versa. System.Text.Json does not support the `[Serializable]` attribute.

[Migrate to System.Text.Json (JSON)](./migrate-to-system-text-json.md).

## XML using DataContractSerializer

[`System.Runtime.Serialization.DataContractSerializer`](/dotnet/api/system.runtime.serialization.datacontractserializer) was introduced in .NET Framework 3.0 and is used to serialize and deserialize data sent in Windows Communication Foundation (WCF) messages. `DataContractSerializer` is an XML serializer that **fully supports the serialization programming model that was used by the `BinaryFormatter`**, which means it honors the `[Serializable]` attribute and implementation of `ISerializable`. Hence, it's the serializer that requires the least amount of effort to migrate to. It does, however, require the known types to be specified up-front (but most .NET collections and primitive types are on a default allow-list and don't need to be specified).

While `DataContractSerializer` carries those functional benefits when migrating from BinaryFormatter, it is not as modern or performant as the other choices.

[Migrate to DataContractSerializer (XML)](./migrate-to-datacontractserializer.md).

> [!NOTE]
> Do not confuse `DataContractSerializer` with [`NetDataContractSerializer`](/dotnet/api/system.runtime.serialization.netdatacontractserializer). `NetDataContractSerializer` is also identified as a [dangerous serializer](../binaryformatter-security-guide.md#dangerous-alternatives).

## Binary using MessagePack

[MessagePack](https://msgpack.org/) is a compact binary serialization format, resulting in smaller message sizes compared to JSON and XML. The open source [MessagePack for C#](https://github.com/MessagePack-CSharp/MessagePack-CSharp) library is highly performant and offers built-in super-fast LZ4 compression for an even smaller data size. It works best when data types are annotated with either `DataContractSerializer` or the library's own attributes. It can be configured to support AOT environments, non-public types and members, and read-only types and members.

[Migrate to MessagePack (binary)](./migrate-to-messagepack.md).

## Binary using protobuf-net

The [protobuf-net](https://github.com/protobuf-net/protobuf-net) library is a contract-based serializer for .NET that uses the binary [Protocol Buffers](https://protobuf.dev) serialization format. The API follows typical .NET patterns and is broadly comparable to `XmlSerializer` and `DataContractSerializer`. This popular library is also feature-rich and can handle non-public types and fields, but many scenarios do require applying attributes to members.

[Migrate to protobuf-net (binary)](./migrate-to-protobuf-net.md).
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
title: "BinaryFormatter migration guide: Compatibility Package"
description: "Using the BinaryFormatter compatibility package."
ms.date: 5/31/2024
no-loc: [BinaryFormatter, Serialization]
helpviewer_keywords:
- "BinaryFormatter"
- "serializing objects"
- "serialization"
- "objects, serializing"
---

# BinaryFormatter compatibility package

> [!CAUTION]
> The compatibility package is not supported and unsafe. We strongly recommend against taking a dependency on this package and instead [migrate away from BinaryFormatter](./index.md#migration-topics).

.NET 9+ users who can't migrate away from `BinaryFormatter` can install the unsupported [System.Runtime.Serialization.Formatters](https://www.nuget.org/packages/System.Runtime.Serialization.Formatters) NuGet package and set the `System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization` AppContext switch to `true`.

> [!NOTE]
> Please note that this package doesn't change the type identity of BinaryFormatter. Existing libraries don't need to be updated to depend on this package in order to use it. The only place that needs to depend on this package is the application project.

The package replaces the in-box implementation of BinaryFormatter with a functioning one, including its vulnerabilities and risks. It's meant as a stop gap if you can't wait with migrating to .NET 9 and later while not having replaced the usages of BinaryFormatter yet. We still strongly recommend you [migrate away from BinaryFormatter](./index.md#migration-topics).

```xml
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryFormatterSerialization>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="System.Runtime.Serialization.Formatters" Version="9.0.0-*" />
jeffhandley marked this conversation as resolved.
Show resolved Hide resolved
</ItemGroup>
```

> [!CAUTION]
> The compatibility package is not supported and unsafe. We strongly recommend against taking a dependency on this package and instead [migrate away from BinaryFormatter](./index.md#migration-topics).
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
---
title: "BinaryFormatter migration guide: Functionality reference"
description: "A reference for BinaryFormatter's functionality that may need to be considered during migrations."
ms.date: 5/31/2024
no-loc: [BinaryFormatter, Serialization, WinForms]
dev_langs:
- CSharp
helpviewer_keywords:
- "BinaryFormatter"
- "serializing objects"
- "serialization"
- "objects, serializing"
---

# BinaryFormatter functionality reference

The `BinaryFormatter` was first introduced with the initial release of .NET Framework in 2002. To understand how to replace usage of BinaryFormatter, it helps to know how BinaryFormatter works.

`BinaryFormatter` can serialize any instance of any type that's annotated with `[Serializable]` or implements the `ISerializable` interface.

## Member names

In most common scenario, the type is annotated with `[Serializable]` and the serializer uses reflection to serialize **all fields** (both public and non-public) except those that are annotated with `[NonSerialized]`. By default, the serialized member names will match the type's field names. This historically led to incompatibilities when even private fields are renamed on `[Serializable]` types. During migrations away from BinaryFormatter, it becomes necessary to understand how serialized field names were handled and overridden.

### C# auto properties

For C# auto properties (`{ get; set; }`), the properties are backed by fields generated by the C# compiler, so the names of the serialized fields are compiler generated (and not very human friendly). A C# decompiler (such as [https://sharplab.io/](https://sharplab.io/) or [ILSpy](https://github.com/icsharpcode/ILSpy)) can demonstrate how C# auto properties are presented to the runtime.

```csharp
[Serializable]
internal class PropertySample
{
public string Name { get; set; }
}
```

The previous class is translated by the C# compiler to:

```csharp
[Serializable]
internal class PropertySample
{
private string <Name>k__BackingField;

public string Name
{
get
{
return <Name>k__BackingField;
}
set
{
<Name>k__BackingField = value;
}
}
}
```

In this case, `<Name>k__BackingField` is **the name of the member that `BinaryFormatter` uses in the serialized payload**. It's not possible to use `nameof` or any other C# operator to get this name.

The [ISerializable](/dotnet/api/system.runtime.serialization.iserializable) interface comes with [GetObjectData(SerializationInfo info, StreamingContext context)](/dotnet/api/system.runtime.serialization.iserializable.getobjectdata) method that allows the users to control the names, by using one of the [SerializationInfo.AddValue](/dotnet/api/system.runtime.serialization.serializationinfo.addvalue) methods.

```csharp
// Note lack of any special attribute.
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("Name", this.Name);
}
```

If such customization has been applied, the information needs to be provided during deserialization as well. That's possible by using the **serialization constructor**, where all values are read from `SerializationInfo` with one of the `Get` methods it provides.

```csharp
jeffhandley marked this conversation as resolved.
Show resolved Hide resolved
private PropertySample(SerializationInfo info, StreamingContext context)
{
this.Name = info.GetString("Name");
}
```

> [!NOTE]
> The `nameof` operator was purposely not used here, as the payload can be persisted and the property can get renamed at a later time. So even if it gets renamed (say to `FirstName` because you decide to also introduce a `LastName` property), to remain backward compatible, the serialization should still use the old name that could have been persisted somewhere.

## Serialization binder

It's recommended to use [SerializationBinder](/dotnet/api/system.runtime.serialization.serializationbinder) to control class loading and mandate what class to load. That minimizes security vulnerabilities (so only allowed types get loaded, even if the attacker modifies the payload to deserialize and load something else).

Using this type requires inheriting from it and overriding the [Type BindToType(string assemblyName, string typeName)](/dotnet/api/system.runtime.serialization.serializationbinder.bindtotype#system-runtime-serialization-serializationbinder-bindtotype(system-string-system-string)) method.

Ideally the list of serializable types is closed set because it means you know which types can be instantiated which will help reduce security vulnerabilities.

Check failure on line 89 in docs/standard/serialization/binaryformatter-migration-guide/functionality-reference.md

View workflow job for this annotation

GitHub Actions / lint

Trailing spaces

docs/standard/serialization/binaryformatter-migration-guide/functionality-reference.md:89:160 MD009/no-trailing-spaces Trailing spaces [Expected: 0 or 2; Actual: 1] https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md009.md
Loading
Loading