### Install Nerdbank.MessagePack via NuGet
Source: https://context7.com/aarnott/nerdbank.messagepack/llms.txt
Install the core library or integration packages for SignalR and ASP.NET Core MVC using NuGet. Ensure C# language version 14 is set for optimal experience.
```xml
```
```xml
14
```
--------------------------------
### SignalR Hub Implementation Example
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/docs/signalr.md
Example implementation of a SignalR Hub. Hubs function identically regardless of the underlying protocol (JSON or MessagePack).
```csharp
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
}
```
--------------------------------
### Custom BinaryData Converter
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/docs/custom-converters.md
This example demonstrates a fully defined converter for System.BinaryData. It serializes BinaryData by writing its underlying byte array.
```csharp
using System;
using System.Buffers;
using Nerdbank.MessagePack;
public class BinaryDataConverter : IMessagePackConverter
{
public void Serialize(ref MessagePackWriter writer, BinaryData value, MessagePackSerializerOptions options)
{
// Write the underlying byte array of BinaryData
writer.Write(value.ToArray());
}
public BinaryData Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
{
// Read the byte array and construct BinaryData
return BinaryData. fromBytes(reader.ReadBytes().ToArray());
}
}
```
--------------------------------
### Example Violation of UseComparerAttribute
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/analyzers/NBMsgPack072.md
This code demonstrates a violation where the UseComparerAttribute is not specifying a compatible comparer. This will be flagged by the analyzer.
```csharp
using System.Collections.Generic;
using Nerdbank.MessagePack;
public class Defective
{
[UseComparer(typeof(int))] // Error: int does not implement IComparer or IEqualityComparer
public List Strings { get; set; }
}
```
--------------------------------
### Example violation: Missing GetJsonSchema
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/analyzers/NBMsgPack032.md
This converter for MyType serializes to a single integer but does not document this schema.
```csharp
public class MyTypeConverter : MessagePackConverter
{
public override MyType Read(ref MessagePackReader reader, SerializationContext context)
{
int a = reader.ReadInt32();
return new MyType(a);
}
public override void Write(ref MessagePackWriter writer, in MyType value, SerializationContext context)
{
writer.Write(value.A);
}
}
```
--------------------------------
### Complete Sample with TypeShapeAttribute
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/docs/surrogate-types.md
This C# code demonstrates the complete setup, including the original type, its surrogate, the marshaler, and the `TypeShapeAttribute` applied to the original type to reference the marshaler.
```csharp
[TypeShape(Marshaler = typeof(OriginalTypeMarshaler))]
public class OriginalType
{
private int _privateField;
private string _anotherPrivateField;
public OriginalType(int privateField, string anotherPrivateField)
{
_privateField = privateField;
_anotherPrivateField = anotherPrivateField;
}
// Public properties for demonstration, but fields are private
public int PublicFieldForDemo { get => _privateField; }
public string PublicStringForDemo { get => _anotherPrivateField; }
}
public record struct OriginalTypeSurrogate(int PrivateField, string AnotherPrivateField);
public class OriginalTypeMarshaler : IMarshaler
{
public OriginalTypeSurrogate? Marshal(OriginalType? value)
{
if (value == null)
{
return null;
}
// Accessing private fields via nested class
return new OriginalTypeSurrogate(value._privateField, value._anotherPrivateField);
}
public OriginalType Unmarshal(OriginalTypeSurrogate? value)
{
if (value == null)
{
// This case should ideally not happen if OriginalType is not nullable
// and the marshaler is correctly used.
throw new InvalidOperationException("Cannot unmarshal null surrogate to a non-nullable OriginalType.");
}
// Creating a new instance of OriginalType using its constructor
return new OriginalType(value.Value.PrivateField, value.Value.AnotherPrivateField);
}
}
```
--------------------------------
### Defective Async Converter Implementation
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/analyzers/NBMsgPack035.md
This example shows a defective implementation where a MessagePackStreamingReader is created and then switched to a MessagePackReader, but neither reader is returned, leading to a bug.
```csharp
public override async ValueTask ReadAsync(MessagePackReader reader, CancellationToken cancellationToken) {
var streamingReader = MessagePackAsyncReader.CreateStreamingReader(reader);
var bufferedReader = MessagePackAsyncReader.CreateBufferedReader(reader);
// Bug: Neither reader is returned.
return default;
}
```
--------------------------------
### Serialize with Key and Gaps
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/docs/customizing-serialization.md
Demonstrates serializing an object with a large gap between assigned indexes, illustrating how MessagePack handles sparse data. This example assumes SerializeDefaultValues is set to Never.
```csharp
public class WithKeyAndGaps
{
public string OneProperty { get; set; } = "value1";
public string AnotherProperty { get; set; } = null;
}
```
--------------------------------
### Corrected Async Converter Implementation
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/analyzers/NBMsgPack034.md
This corrected example shows the proper way to handle MessagePackWriter. All statements using the writer must occur before it is returned. If the writer is needed after an await, create a new one.
```csharp
public override async ValueTask WriteAsync(TBufferWriter writer, T value, CancellationToken cancellationToken) where TBufferWriter : IBufferWriter
{
await base.WriteAsync(writer, value, cancellationToken);
// Fix: All statements using the writer occur before returning it.
MessagePackAsyncWriter.ReturnWriter(writer);
// If the sync writer must be used after the await expression as well, you may create a new sync writer after the await statement.
// writer.Write(123);
}
```
--------------------------------
### Resolution: Return Reader After Use
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/analyzers/NBMsgPack036.md
This example shows the correct way to handle MessagePack readers. The MessagePackAsyncReader.ReturnReader call is moved to a point after the reader is no longer needed, preventing reuse issues.
```csharp
internal class Fix : IMessagePackSerializationActivator
{
public async ValueTask ActivateAsync(MessagePackSerializerOptions options, Type type, CancellationToken cancellationToken)
{
await using var reader = options.ReaderPool.Rent(cancellationToken);
await reader.ReadAsync(cancellationToken);
var result = await reader.ReadAsync(cancellationToken); // Use reader before returning
options.ReaderPool.ReturnReader(reader);
return result;
}
}
```
--------------------------------
### Defective Async Converter Implementation
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/analyzers/NBMsgPack034.md
This example demonstrates a violation where MessagePackWriter is reused after being returned. Ensure all writer operations complete before returning the writer.
```csharp
public override async ValueTask WriteAsync(TBufferWriter writer, T value, CancellationToken cancellationToken) where TBufferWriter : IBufferWriter
{
await base.WriteAsync(writer, value, cancellationToken);
// Defect: Reusing writer after returning it.
MessagePackAsyncWriter.ReturnWriter(writer);
writer.Write(123);
}
```
--------------------------------
### Stateful Converter with SerializationContext
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/docs/custom-converters.md
This C# example shows how a custom converter can access state from the SerializationContext instead of using fields, enabling concurrent use with different state values and avoiding serializer instantiation costs.
```csharp
public sealed class StatefulConverter : MessagePackConverter
{
public override void Write(ref MessagePackWriter writer, T value, MessagePackSerializerOptions options)
{
// Retrieve state from the context
var connectionId = options.Context.Items.GetValueOrDefault("connectionId");
// ... use connectionId for serialization logic ...
MessagePackSerializer.Default.Serialize(ref writer, value, options);
}
public override T Read(ref MessagePackReader reader, MessagePackSerializerOptions options)
{
// Retrieve state from the context
var connectionId = options.Context.Items.GetValueOrDefault("connectionId");
// ... use connectionId for deserialization logic ...
return MessagePackSerializer.Default.Deserialize(ref reader, options);
}
}
```
--------------------------------
### Duck Typing for Unions
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/docs/unions.md
Example demonstrating duck typing for unions, where types are identified by their shape (members) rather than explicit type headers. This is useful for protocol compatibility.
```csharp
public abstract class Animal
{
public string Name { get; set; }
}
[DerivedTypeDuckTyping]
public class Dog : Animal
{
public int BarkVolume { get; set; }
}
[DerivedTypeDuckTyping]
public class Cat : Animal
{
public int MeowPitch { get; set; }
}
```
--------------------------------
### Example Violation: Async Converter Missing PreferAsyncSerialization Override
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/analyzers/NBMsgPack037.md
This converter overrides async methods but fails to set `PreferAsyncSerialization` to `true`. This can lead to the serializer using slower synchronous methods.
```csharp
public class DefectiveConverter : MessagePackConverter
{
public override T Read(ref MessagePackReader reader, MessagePackSerializerOptions options)
{
throw new System.NotImplementedException();
}
public override async ValueTask ReadAsync(ref MessagePackReader reader, MessagePackSerializerOptions options, CancellationToken cancellationToken = default)
{
await Task.Delay(1, cancellationToken);
return default;
}
public override void Write(ref MessagePackWriter writer, T value, MessagePackSerializerOptions options)
{
throw new System.NotImplementedException();
}
public override async ValueTask WriteAsync(ref MessagePackWriter writer, T value, MessagePackSerializerOptions options, CancellationToken cancellationToken = default)
{
await Task.Delay(1, cancellationToken);
}
}
```
--------------------------------
### Implement Stateful MessagePack Converter using SerializationContext
Source: https://context7.com/aarnott/nerdbank.messagepack/llms.txt
Utilize SerializationContext to store per-call state, enabling a single serializer instance to handle multiple configurations. This avoids recreating the converter pipeline for different states. The example demonstrates reading and writing values with a dynamic multiplier.
```csharp
using Nerdbank.MessagePack;
using PolyType;
[GenerateShape]
[MessagePackConverter(typeof(StatefulConverter))]
public partial record struct SpecialType(int Value);
public class StatefulConverter : MessagePackConverter
{
public override SpecialType Read(ref MessagePackReader reader, SerializationContext context)
{
int multiplier = (int)context["ValueMultiplier"]!;
return new SpecialType(reader.ReadInt32() / multiplier);
}
public override void Write(ref MessagePackWriter writer, in SpecialType value, SerializationContext context)
{
int multiplier = (int)context["ValueMultiplier"]!;
writer.Write(value.Value * multiplier);
}
}
MessagePackSerializer serializer = new()
{
StartingContext = new SerializationContext { ["ValueMultiplier"] = 3 },
};
byte[] msgpack = serializer.Serialize(new SpecialType(5));
Console.WriteLine(serializer.ConvertToJson(msgpack)); // 15 (5 * 3)
SpecialType back = serializer.Deserialize(msgpack);
Console.WriteLine(back.Value); // 5 (15 / 3)
```
--------------------------------
### Resolution: Private UnusedDataPacket property
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/analyzers/NBMsgPack062.md
This example demonstrates the correct way to declare an UnusedDataPacket property as private. Remember to add a PropertyShapeAttribute to avoid introducing NBMsgPack060 issues.
```cs
public class Person
{
public required string Name { get; set; }
[PropertyShape]
private UnusedDataPacket Extension { get; set; }
}
```
--------------------------------
### Violation: Public UnusedDataPacket property
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/analyzers/NBMsgPack062.md
This example shows a violation where the UnusedDataPacket property is declared as public. This can lead to issues with message packing.
```cs
public class Person
{
public required string Name { get; set; }
public UnusedDataPacket Extension { get; set; } // NBMsgPack062
}
```
--------------------------------
### Example Violation of UseComparerAttribute Rule
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/analyzers/NBMsgPack073.md
This snippet demonstrates a violation where UseComparerAttribute is applied with an abstract type without a static member. This will trigger the NBMsgPack073 analyzer.
```csharp
using Nerdbank.MessagePack;
public abstract class AbstractComparer
{
}
public class MyClass
{
[UseComparer(typeof(AbstractComparer))]
public string MyProperty { get; set; }
}
```
--------------------------------
### Violation: Custom converter calls MessagePackSerializer
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/analyzers/NBMsgPack030.md
This example demonstrates an incorrect implementation where a custom converter instantiates a new MessagePackSerializer and uses it to serialize a property. This violates the recommended practice for custom converters.
```csharp
public class MyTypeConverter : MessagePackConverter
{
public override MyType? Read(ref MessagePackReader reader, SerializationContext context) => throw new System.NotImplementedException();
public override void Write(ref MessagePackWriter writer, in MyType? value, SerializationContext context)
{
var serializer = new MessagePackSerializer(); // NBMsgPack030
serializer.Serialize(value.SomeProperty); // NBMsgPack030
}
}
public class MyType
{
public SomeOtherType? SomeProperty { get; set; }
}
[GenerateShape]
public partial class SomeOtherType {}
```
--------------------------------
### Example Violation: Missing ref modifier
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/analyzers/NBMsgPack050.md
This code demonstrates a method declared with a ref struct parameter without the `ref` modifier, which can cause issues with buffer and position tracking.
```csharp
public void DefectiveMethod(MessagePackReader reader)
{
// This method will operate on a copy of the reader, not the original.
// Any changes to reader.Position or its internal buffers will be lost.
}
```
--------------------------------
### Use Surrogate Types with IMarshaler for Non-Serializable Objects
Source: https://context7.com/aarnott/nerdbank.messagepack/llms.txt
Make non-serializable types serializable by pairing them with a simple, serializable surrogate record and an IMarshaler implementation. This approach is simpler than writing a full custom converter. The example shows how to marshal an 'OriginalType' to a 'MarshaledType' and back.
```csharp
using Nerdbank.MessagePack;
using PolyType;
[GenerateShape]
[TypeShape(Marshaler = typeof(MyTypeMarshaler))]
public partial class OriginalType
{
private int a;
private int b;
public OriginalType(int a, int b) { this.a = a; this.b = b; }
public int Sum => this.a + this.b;
// Surrogate — simple serializable struct
internal record struct MarshaledType(int A, int B);
// Marshaler — converts between OriginalType and MarshaledType
internal class MyTypeMarshaler : IMarshaler
{
public MarshaledType? Marshal(OriginalType? value)
=> value is null ? null : new(value.a, value.b);
public OriginalType? Unmarshal(MarshaledType? surrogate)
=> surrogate.HasValue ? new(surrogate.Value.A, surrogate.Value.B) : null;
}
}
MessagePackSerializer serializer = new();
var original = new OriginalType(3, 4);
byte[] msgpack = serializer.Serialize(original);
OriginalType? restored = serializer.Deserialize(msgpack);
Console.WriteLine(restored!.Sum); // 7
```
--------------------------------
### Avoid Assembly Loads with ITypeShape
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/docs/performance.md
Use serialization overloads that accept ITypeShape to prevent unnecessary assembly loads. This example demonstrates how to serialize a type without loading the assembly for BigInteger.
```csharp
using Nerdbank.MessagePack;
using System.Numerics;
// Assume MyType is defined elsewhere and does not directly depend on System.Numerics
// Assume MyType implements ITypeShape
var serializer = new MessagePackSerializer();
// This call will load the System.Numerics assembly if BigInteger is referenced by any witness type
// var data = serializer.Serialize(new MyType());
// Instead, use the TypeShape provider directly to avoid loading unrelated assemblies:
var serializer2 = new MessagePackSerializer(TypeShape.Of());
var data2 = serializer2.Serialize(new MyType());
```
--------------------------------
### Implement Custom MessagePack Converter for a Record
Source: https://context7.com/aarnott/nerdbank.messagepack/llms.txt
Subclass MessagePackConverter to define custom serialization logic for types like 'Point'. Ensure to call context.DepthStep() before reading or writing structures to prevent stack overflows. This example shows how to handle nulls, map headers, and unknown fields for forward compatibility.
```csharp
using Nerdbank.MessagePack;
using PolyType;
public record Point(int X, int Y);
public class PointConverter : MessagePackConverter
{
public override Point? Read(ref MessagePackReader reader, SerializationContext context)
{
if (reader.TryReadNil()) return null;
context.DepthStep();
int count = reader.ReadMapHeader();
int x = 0, y = 0;
for (int i = 0; i < count; i++)
{
switch (reader.ReadString())
{
case "x": x = reader.ReadInt32(); break;
case "y": y = reader.ReadInt32(); break;
default: reader.Skip(context); break; // forward-compat: skip unknowns
}
}
return new Point(x, y);
}
public override void Write(ref MessagePackWriter writer, in Point? value, SerializationContext context)
{
if (value is null) { writer.WriteNil(); return; }
context.DepthStep();
writer.WriteMapHeader(2);
writer.Write("x"); writer.Write(value.X);
writer.Write("y"); writer.Write(value.Y);
}
}
// Register at the type level via attribute
[MessagePackConverter(typeof(PointConverter))]
[GenerateShape]
public partial class AnnotatedPoint { public int X; public int Y; }
// Or register at runtime
MessagePackSerializer serializer = new();
serializer = serializer with
{
Converters = [.. serializer.Converters, new PointConverter()],
};
byte[] msgpack = serializer.Serialize(new Point(3, 4));
Console.WriteLine(serializer.ConvertToJson(msgpack)); // {"x":3,"y":4}
```
--------------------------------
### Serve Documentation Locally with DocFX
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/CONTRIBUTING.md
Run this command in the 'docfx/' directory to host the documentation site locally. Rebuild the docs by running `dotnet docfx` again after making changes.
```powershell
dotnet docfx --serve
```
--------------------------------
### Example Violation of DerivedTypeShapeAttribute
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/analyzers/NBMsgPack010.md
Illustrates a scenario where DerivedType does not derive from BaseType, violating the assignability requirement of DerivedTypeShapeAttribute.
```cs
[DerivedTypeShape(typeof(DerivedType), Tag = 1)] // DerivedType is not in fact derived from BaseType
class BaseType
{
}
[GenerateShape]
class DerivedType
{
}
```
--------------------------------
### Example Violation of NBMsgPack060
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/analyzers/NBMsgPack060.md
This C# code demonstrates a violation where a private member of type UnusedDataPacket is not configured with a PropertyShapeAttribute.
```csharp
public class Person
{
public required string Name { get; set; }
private UnusedDataPacket Extension { get; set; } // NBMsgPack060
}
```
--------------------------------
### Serialize and Deserialize a Record
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/docs/getting-started.md
Demonstrates the roundtrip serialization and deserialization of a `SimpleRecord` object using Nerdbank.MessagePack. This involves creating an instance, serializing it to a byte array, and then deserializing it back.
```csharp
// Create an instance of the record.
var original = new SimpleRecord { Name = "Example", Value = 42 };
// Serialize the object to MessagePack format.
var buffer = MessagePackSerializer.Serialize(original);
// Deserialize the MessagePack data back into an object.
var roundtrip = MessagePackSerializer.Deserialize(buffer);
// Verify the roundtrip.
Assert.Equal(original.Name, roundtrip.Name);
Assert.Equal(original.Value, roundtrip.Value);
```
--------------------------------
### Violation: Reusing Reader After Return
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/analyzers/NBMsgPack036.md
This example demonstrates a violation where a MessagePackStreamingReader is returned before the method has finished using it. This can cause issues with subsequent read operations.
```csharp
internal class Defective : IMessagePackSerializationActivator
{
public async ValueTask ActivateAsync(MessagePackSerializerOptions options, Type type, CancellationToken cancellationToken)
{
await using var reader = options.ReaderPool.Rent(cancellationToken);
await reader.ReadAsync(cancellationToken);
options.ReaderPool.ReturnReader(reader);
await reader.ReadAsync(cancellationToken); // Violation: reader is already returned
return null;
}
}
```
--------------------------------
### Original Type with Non-Serializable Fields
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/docs/surrogate-types.md
This C# code defines a type with private fields that are not serialized by default. It serves as an example of a type that requires a surrogate for serialization.
```csharp
public class OriginalType
{
private int _privateField;
private string _anotherPrivateField;
public OriginalType(int privateField, string anotherPrivateField)
{
_privateField = privateField;
_anotherPrivateField = anotherPrivateField;
}
// Public properties for demonstration, but fields are private
public int PublicFieldForDemo { get => _privateField; }
public string PublicStringForDemo { get => _anotherPrivateField; }
}
```
--------------------------------
### Set C# Language Version
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/docs/getting-started.md
Configure your project to use C# 14 for enhanced features. This is typically done in your .csproj or Directory.Build.props file.
```xml
14
```
--------------------------------
### Resolution: Multi-Targeting Project Fix
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/analyzers/NBMsgPack051.md
In multi-targeting projects, use the preferred API only where it is available. This avoids breaking builds for older target frameworks and reduces the need for extensive `#if` sections.
```csharp
#if NET6_0_OR_GREATER
unionTypeMapping.Map(Shape.Instance);
#else
unionTypeMapping.Map();
#endif
```
--------------------------------
### Violation: MessagePackConverter with Non-Public Constructor
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/analyzers/NBMsgPack021.md
This example demonstrates the violation where a custom MessagePackConverter type has an explicit, non-public constructor. Ensure your converter type has a public default constructor.
```cs
[MessagePackConverter(typeof(MyTypeConverter))]
public class MyType
{
}
public class MyTypeConverter : MessagePackConverter
{
private MyTypeConverter() { }
public override MyType Read(ref MessagePackReader reader, SerializationContext context) => throw new System.NotImplementedException();
public override void Write(ref MessagePackWriter writer, in MyType value, SerializationContext context) => throw new System.NotImplementedException();
}
```
--------------------------------
### Array-Based Serialization with [Key] Attribute
Source: https://context7.com/aarnott/nerdbank.messagepack/llms.txt
Use the [Key(n)] attribute to serialize properties as compact msgpack arrays instead of string-keyed maps. Do not reuse key indexes after deployment to maintain backward compatibility.
```csharp
using Nerdbank.MessagePack;
using PolyType;
[GenerateShape]
public partial class Product
{
[Key(0)]
public string? Name { get; set; }
[Key(1)]
public decimal Price { get; set; }
[Key(2)]
public int Stock { get; set; }
}
// Serializes as: ["Widget",9.99,100] (array, not name=value map)
MessagePackSerializer serializer = new();
byte[] msgpack = serializer.Serialize(new Product { Name = "Widget", Price = 9.99m, Stock = 100 });
Console.WriteLine(serializer.ConvertToJson(msgpack)); // ["Widget",9.99,100]
```
--------------------------------
### Serialize objects with indexes for keys in C#
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/docs/customizing-serialization.md
Apply @Nerdbank.MessagePack.KeyAttribute to properties and fields to opt into serializing with indexes instead of property names. This results in more compact MessagePack data.
```csharp
public class MyPoco
{
[Key(0)]
public string OneProperty { get; set; }
[Key(1)]
public string AnotherProperty { get; set; }
}
```
--------------------------------
### Runtime Derived Type Registration (.NET)
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/docs/unions.md
Shows how to register derived types for a union at runtime in .NET, useful when static attribute registration is not possible.
```csharp
var builder = new MessagePackSerializerOptions.Builder { Security = { RecursiveDepth = 100 } };
builder.WithDerivedTypes(new DerivedType[] {
new DerivedType(typeof(Dog), "dog"),
new DerivedType(typeof(Cat), "cat"),
new DerivedType(typeof(FarmAnimal), "animal"),
});
var serializer = new MessagePackSerializer(builder.Build());
```
--------------------------------
### Resolution: Implementing GetJsonSchema
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/analyzers/NBMsgPack032.md
This converter for MyType correctly implements GetJsonSchema to document that it serializes as an integer.
```csharp
public class MyTypeConverter : MessagePackConverter
{
public override MyType Read(ref MessagePackReader reader, SerializationContext context)
{
int a = reader.ReadInt32();
return new MyType(a);
}
public override void Write(ref MessagePackWriter writer, in MyType value, SerializationContext context)
{
writer.Write(value.A);
}
public override JsonObject GetJsonSchema(JsonSchemaContext context, ITypeShape typeShape)
{
return new JsonObject
{
["type"] = "integer",
};
}
}
```
--------------------------------
### DerivedTypeShape Alias Resolution
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/analyzers/NBMsgPack011.md
This example shows the correct way to define DerivedTypeShape attributes by assigning unique aliases (Tags) to each derived type. This resolves the violation shown previously.
```csharp
[DerivedTypeShape(typeof(DerivedType1), Tag = 1)]
[DerivedTypeShape(typeof(DerivedType2), Tag = 2)]
class BaseType
{
}
class DerivedType1 : BaseType
{
}
class DerivedType2 : BaseType
{
}
```
--------------------------------
### Resolution: Unique DerivedTypeShape Alias
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/analyzers/NBMsgPack012.md
This example demonstrates the correct usage where each derived type is assigned a unique tag, resolving the violation. This ensures proper functioning of subtype unions.
```cs
[DerivedTypeShape(typeof(DerivedType), Tag = 1)]
class BaseType
{
}
class DerivedType : BaseType
{
}
```
--------------------------------
### Add Nerdbank.MessagePack Namespace
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/docs/unity.md
Include this using statement at the top of your code files to make the serializer's extension methods available. This is particularly useful for projects targeting non-.NET platforms.
```csharp
using Nerdbank.MessagePack;
```
--------------------------------
### Violation: Duplicate DerivedTypeShape Alias
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/analyzers/NBMsgPack012.md
This example shows a violation where the `DerivedType` is assigned two different tags within the same `BaseType` scope. This can cause issues with subtype union resolution.
```cs
[DerivedTypeShape(typeof(DerivedType), Tag = 1)]
[DerivedTypeShape(typeof(DerivedType), Tag = 2)] // assigned second alias to a subtype
class BaseType
{
}
class DerivedType : BaseType
{
}
```
--------------------------------
### Configure SignalR Server with Witness Class
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/docs/signalr.md
Configure the SignalR server to use MessagePack protocol, utilizing a witness class for type shape provision. This is a preferred approach for type safety with the PolyType source generator.
```csharp
var builder = WebApplication.CreateBuilder();
builder.Services.AddSignalR()
.AddMessagePackProtocol();
var app = builder.Build();
app.MapHub("/chat");
app.Run();
```
--------------------------------
### Resolution: Use SerializationContext to get converter
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/analyzers/NBMsgPack030.md
This corrected implementation shows how to properly delegate the serialization of a sub-property. It retrieves the appropriate converter for the sub-type from the SerializationContext and uses it to write the value.
```csharp
public override void Write(ref MessagePackWriter writer, in MyType? value, SerializationContext context)
{
SomeOtherType? someProperty = value.SomeProperty;
context.GetConverter().Write(ref writer, ref someProperty, context);
}
```
--------------------------------
### Configure SignalR Client with MessagePack Protocol
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/docs/signalr.md
Configure a .NET SignalR client to use the MessagePack protocol by adding it to the connection builder. Ensure both server and client use the same protocol.
```csharp
var connection = new HubConnectionBuilder()
.WithUrl("/chat")
.AddMessagePackProtocol()
.Build();
```
--------------------------------
### Enable MessagePack Output for Controller Actions
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/docs/aspnetmvc.md
Apply the [Produces("application/x-msgpack")] attribute to controller actions or the entire controller to indicate that it will return data in the MessagePack format. This ensures clients can request and receive MessagePack-encoded data.
```csharp
[Produces("application/x-msgpack")]
public class PersonController : Controller
{
public IActionResult GetPerson(int id)
{
// ... return a Person object
return Ok(new Person { Id = id, Name = "John Doe" });
}
}
```
--------------------------------
### Using MessagePackString for Property Names
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/docs/custom-converters.md
This C# code demonstrates how to use the MessagePackString class to avoid repeated encoding and allocation of string property names, reducing GC pressure and improving performance.
```csharp
public sealed class User
{
public int Age { get; set; }
public string Name { get; set; }
}
public sealed class UserConverter : MessagePackConverter
{
private static readonly MessagePackString AgeKey = new MessagePackString("age");
private static readonly MessagePackString NameKey = new MessagePackString("name");
public override void Write(ref MessagePackWriter writer, User value, MessagePackSerializerOptions options)
{
writer.WriteObjectHeader(2);
writer.Write(AgeKey);
writer.Write(value.Age);
writer.Write(NameKey);
writer.Write(value.Name);
}
public override User Read(ref MessagePackReader reader, MessagePackSerializerOptions options)
{
var count = reader.ReadObjectHeader();
var user = new User();
for (var i = 0; i < count; i++)
{
var key = reader.ReadString();
if (key.Equals(AgeKey.AsSpan()))
{
user.Age = reader.ReadInt32();
}
else if (key.Equals(NameKey.AsSpan()))
{
user.Name = reader.ReadString();
}
else
{
reader.Skip();
}
}
return user;
}
}
```
--------------------------------
### DerivedTypeShape Alias Violation
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/analyzers/NBMsgPack011.md
This example demonstrates a violation where the same alias (Tag = 1) is reused for two different derived types within the same base type. Ensure aliases are unique to avoid this.
```csharp
[DerivedTypeShape(typeof(DerivedType1), Tag = 1)]
[DerivedTypeShape(typeof(DerivedType2), Tag = 1)] // Reused an alias
class BaseType
{
}
class DerivedType1 : BaseType
{
}
class DerivedType2 : BaseType
{
}
```
--------------------------------
### Example Violation of MessagePackConverter Type
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/analyzers/NBMsgPack020.md
This code demonstrates an incorrect usage of the MessagePackConverter attribute where the specified converter type does not derive from the required base class. This will cause a compilation or runtime error.
```cs
[MessagePackConverter(typeof(MyTypeConverter))] // MyTypeConverter does not derive from the correct base type
public class MyType
{
}
public class MyTypeConverter
{
public MyType Read(ref MessagePackReader reader, SerializationContext context) => throw new System.NotImplementedException();
public void Write(ref MessagePackWriter writer, ref MyType value, SerializationContext context) => throw new System.NotImplementedException();
}
```
--------------------------------
### Enable System.Text.Json Converters
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/docs/built-in-converters.md
Use this code to create a msgpack serializer that can convert System.Text.Json DOM types. This enables support for JsonNode, JsonElement, and JsonDocument.
```csharp
var serializer = new MessagePackSerializerOptions
.Standard
.WithSystemTextJsonConverters()
.ToSerializerOptions();
var msgpack = new MessagePackSerializer(serializer);
```
--------------------------------
### Simple Targeted Deserialization
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/docs/targeted-deserialization.md
Deserialize a specific property from a msgpack object using a LINQ expression to define the path. This reduces the cost of deserialization by only processing the necessary data.
```csharp
var data = new byte[] { ... }; // msgpack encoded data
var name = MessagePackSerializer.DeserializePath(data, "person.name");
```
--------------------------------
### Read Whole Array for Version Compatibility
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/docs/custom-converters.md
Ensure version compatibility by reading all elements of an array, even if fewer are expected. This prevents errors when deserializing data serialized by different converter versions.
```csharp
public override MyType Read(ref MessagePackReader reader, MessagePackSerializerOptions options)
{
// ... other cases ...
case 1:
// Read and skip value at index 1
reader.Skip();
break;
// ... other cases ...
default:
// Skip any unknown array index
reader.Skip();
break;
}
return new MyType(/* ... */);
}
```
--------------------------------
### DerivedTypeShape Aliases Across Different Types
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/analyzers/NBMsgPack011.md
This example illustrates that DerivedTypeShape aliases do not need to be unique across different base types. The same alias (Tag = 1) can be used for derived types of distinct base classes.
```csharp
[DerivedTypeShape(typeof(DerivedFromBaseType), Tag = 1)]
class BaseType
{
}
[DerivedTypeShape(typeof(DerivedFromAnotherType), Tag = 1)]
class AnotherType
{
}
```
--------------------------------
### Resolution: MessagePackConverter with Public Constructor
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/analyzers/NBMsgPack021.md
This example shows the corrected version where the custom MessagePackConverter type declares a public default constructor. Alternatively, remove the explicit constructor to allow the C# compiler to generate one.
```cs
[MessagePackConverter(typeof(MyTypeConverter))]
public class MyType
{
}
public class MyTypeConverter : MessagePackConverter
{
public override MyType Read(ref MessagePackReader reader, SerializationContext context) => throw new System.NotImplementedException();
public override void Write(ref MessagePackWriter writer, in MyType value, SerializationContext context) => throw new System.NotImplementedException();
}
```
--------------------------------
### Resolution: Flexible deserialization with default values
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/analyzers/NBMsgPack031.md
This converter demonstrates flexible deserialization by reading any number of elements from an array header and providing default values if fewer than two are encountered. It also correctly skips unknown elements.
```cs
public override MyType Read(ref MessagePackReader reader, SerializationContext context)
{
int count = reader.ReadArrayHeader();
// Initialize default values in case the array has fewer than 2 elements.
int property1 = 0;
short property2 = 0;
for (int i = 0; i < count; i++)
{
switch (i)
{
case 0:
property1 = reader.ReadInt32();
break;
case 1:
property2 = reader.ReadInt16();
break;
default:
// Very important that we read elements in the array belonging to this object even if we don't know what to do with them.
// Calling Skip() is a way to read and drop the element without knowing what kind it is.
reader.Skip();
break;
}
}
return new MyType(property1, property2);
}
```
--------------------------------
### Implement a Converter Factory
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/docs/custom-converters.md
Define a converter factory by implementing IMessagePackConverterFactory to apply a converter to many types. Register the factory using the MessagePackSerializer.ConverterFactories property. The factory is consulted for any data type requiring a converter.
```csharp
using Nerdbank.MessagePack;
var serializer = new MessagePackSerializer(new MessagePackSerializerOptions { ConverterFactories = { new MyConverterFactory() } });
public class MyConverterFactory : IMessagePackConverterFactory
{
public IMessagePackConverter? GetConverter(Type typeToConvert, MessagePackSerializerOptions options)
{
// Logic to determine if a converter should be returned for the given type.
// Example: return new MyCustomConverter(); if typeToConvert has a specific attribute.
return null; // Or return a specific converter instance
}
}
```
--------------------------------
### Example Violation: Union Type Mapping
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/analyzers/NBMsgPack051.md
This code constructs a union type mapping using a method that may fail at runtime if type arguments are not attributed with GenerateShapeAttribute. This is acceptable for .NET Standard or .NET Framework targets but emits a warning for .NET targets.
```csharp
unionTypeMapping.Map();
```
--------------------------------
### Define and Serialize/Deserialize C# Record with Nerdbank.MessagePack
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/README.md
Use the `[GenerateShape]` attribute on your top-level record for performance and safety. This attribute recursively generates source for all referenced types, ensuring full object graph serialization.
```cs
[GenerateShape]
public partial record ARecord(string AString, bool ABoolean, float AFloat, double ADouble);
```
```cs
// Construct a value.
var value = new ARecord("hello", true, 1.0f, 2.0);
// Create a serializer instance.
MessagePackSerializer serializer = new();
// Serialize the value to the buffer.
byte[] msgpack = serializer.Serialize(value);
// Deserialize it back.
var deserialized = serializer.Deserialize(msgpack);
```
--------------------------------
### Basic Round-Trip Serialization with MessagePackSerializer
Source: https://context7.com/aarnott/nerdbank.messagepack/llms.txt
Use MessagePackSerializer for basic serialization and deserialization. Apply [GenerateShape] to the root type for source-generated, NativeAOT-safe serialization. Reuse serializer instances for performance as they cache converter models and are thread-safe.
```csharp
using Nerdbank.MessagePack;
using PolyType;
// Only the top-level type needs [GenerateShape] and partial.
[GenerateShape]
public partial record Person(string Name, int Age, bool IsAdult);
// Create one serializer instance and reuse it.
MessagePackSerializer serializer = new();
var person = new Person("Alice", 30, true);
// Serialize to byte[]
byte[] msgpack = serializer.Serialize(person);
// Deserialize back
Person? deserialized = serializer.Deserialize(msgpack);
Console.WriteLine(deserialized); // Person { Name = Alice, Age = 30, IsAdult = True }
// Inspect the binary as JSON for debugging (not for production use)
string json = serializer.ConvertToJson(msgpack);
Console.WriteLine(json); // {"Name":"Alice","Age":30,"IsAdult":true}
```
--------------------------------
### Runtime Derived Type Registration with DerivedShapeMapping
Source: https://context7.com/aarnott/nerdbank.messagepack/llms.txt
Register derived types at runtime using `DerivedShapeMapping` when attribute-based registration is not possible, such as for third-party types or plugin systems. This allows for polymorphic serialization without compile-time attribute decoration.
```csharp
using Nerdbank.MessagePack;
using PolyType;
record Animal(string Name);
[GenerateShape]
partial record Horse(string Name) : Animal(Name);
[GenerateShape]
partial record Cow(string Name) : Animal(Name);
// Build the mapping imperatively
DerivedShapeMapping mapping = new();
mapping.Add(tag: 1);
mapping.Add(tag: 2);
MessagePackSerializer serializer = new()
{
DerivedTypeUnions = [mapping],
};
byte[] msgpack = serializer.Serialize(new Horse("Flicka"));
Animal back = serializer.Deserialize(msgpack)!; // returns Horse
```
--------------------------------
### Resolution: Concrete Type with UseComparerAttribute
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/analyzers/NBMsgPack070.md
This code shows the correct usage of the UseComparerAttribute by specifying a concrete type, resolving the NBMsgPack070 analyzer violation.
```csharp
using Nerdbank.MessagePack;
public class MyClass
{
[UseComparer(typeof(MyComparer))]
public T Value { get; set; }
}
public class MyComparer : IComparer
{
public int Compare(T x, T y) => 0;
}
```
--------------------------------
### Union Types with Integer Identifiers
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/docs/unions.md
Shows how to use integer identifiers for union cases, which can improve performance and reduce payload size.
```csharp
[Union(0, 1)]
public abstract record Animal
{
[Union(1, 2)]
public abstract record Horse : Animal;
[Union(2, 3)]
public record QuarterHorse : Horse;
[Union(3, 4)]
public record Thoroughbred : Horse;
[Union(4, 5)]
public record Dog : Animal;
[Union(5, 6)]
public record Cat : Animal;
}
```
--------------------------------
### MessagePackSerializer Configuration Properties
Source: https://context7.com/aarnott/nerdbank.messagepack/llms.txt
Configure MessagePackSerializer via object initializer or 'with' expressions. The instance is immutable and thread-safe. Options include property naming policy, skipping default values, serializing enums by name, performance optimization, max nesting depth, and preserving object references.
```csharp
using Nerdbank.MessagePack;
// Apply camelCase naming to all properties
var serializer = new MessagePackSerializer
{
PropertyNamingPolicy = MessagePackNamingPolicy.CamelCase,
};
// Skip default-valued properties to reduce payload size
serializer = serializer with
{
SerializeDefaultValues = SerializeDefaultValuesPolicy.Never,
};
// Serialize enum values as their string names instead of ordinals
serializer = serializer with
{
SerializeEnumValuesByName = true,
};
// Boost performance when backward compat with older serialized data is not needed
serializer = serializer with
{
PerfOverSchemaStability = true,
};
// Set max nesting depth for security (default is conservative)
serializer = new MessagePackSerializer
{
StartingContext = new SerializationContext { MaxDepth = 100 },
};
// Preserve object reference equality across serialize/deserialize
serializer = serializer with { PreserveReferences = true };
```
--------------------------------
### Configure SignalR with MessagePack Protocol
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/docs/signalr.md
Add the MessagePack protocol to your SignalR hub configuration by calling AddMessagePackProtocol. This should be done in your builder class.
```csharp
builder.Services.AddSignalR().AddMessagePackProtocol();
```
--------------------------------
### Configure SignalR Client with Custom Serializer
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/docs/signalr.md
Configure a .NET SignalR client to use MessagePack protocol with a custom serializer. This allows for specific MessagePack options, such as compression, to be applied on the client side.
```csharp
var connection = new HubConnectionBuilder()
.WithUrl("/chat")
.AddMessagePackProtocol(options => {
options.SerializerOptions = MessagePackSerializerOptions.Standard.WithCompression(MessagePackCompression.Lz4Block);
})
.Build();
```
--------------------------------
### Violation: Open Generic Type with UseComparerAttribute
Source: https://github.com/aarnott/nerdbank.messagepack/blob/main/docfx/analyzers/NBMsgPack070.md
This code demonstrates the incorrect usage of the UseComparerAttribute with an open generic type, which will trigger the NBMsgPack070 analyzer.
```csharp
using Nerdbank.MessagePack;
public class MyClass
{
[UseComparer(typeof(MyComparer<>))]
public T Value { get; set; }
}
public class MyComparer : IComparer
{
public int Compare(T x, T y) => 0;
}
```