PicoMsgPack 2026.4.2

There is a newer version of this package available.
See the version list below for details.
dotnet add package PicoMsgPack --version 2026.4.2
                    
NuGet\Install-Package PicoMsgPack -Version 2026.4.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="PicoMsgPack" Version="2026.4.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="PicoMsgPack" Version="2026.4.2" />
                    
Directory.Packages.props
<PackageReference Include="PicoMsgPack" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add PicoMsgPack --version 2026.4.2
                    
#r "nuget: PicoMsgPack, 2026.4.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package PicoMsgPack@2026.4.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=PicoMsgPack&version=2026.4.2
                    
Install as a Cake Addin
#tool nuget:?package=PicoMsgPack&version=2026.4.2
                    
Install as a Cake Tool

PicoSerDe

AOT-first, reflection-free serialization framework. Five formats, one unified API. Source-generated ref struct readers/writers with zero heap allocation on the hot path — deployable under NativeAOT and trimming where many serialization libraries cannot run.

CI NuGet License: MIT


Modules

Format Package Status AOT Readme
JSON PicoJetson ✅ Production ✅ →
MessagePack PicoMsgPack ✅ Production ✅ →
INI PicoIni ✅ Production ✅ →
TOML PicoToml ✅ Production ✅ →
YAML PicoYaml ✅ Production ✅ →

PicoYaml is the only AOT-compatible YAML library on .NET.


Test Coverage

781 tests across all 6 modules, with cross-validation against 5 competitor libraries:

Module Tests Competitor Cross-Validation
PicoJetson 324 System.Text.Json ✅ bidirectional, all 19 property types
PicoToml 90 Tomlyn ✅ bidirectional, 20 property types, NestedList via [[key]]
PicoYaml 90 YamlDotNet ✅ bidirectional, 19 property types, DateOnly/TimeOnly conerters
PicoIni 104 Microsoft.Extensions.Configuration.Ini ✅ bidirectional, 16 property types
PicoMsgPack 113 MessagePack-CSharp ✅ map/array dual-format, 14 property types
PicoSerDe.Core 36 — —

Performance Summary

Numbers below are PicoSerDe on NativeAOT vs competitors on JIT — the competitors cannot run under NativeAOT at all. In a JIT environment, mature reflection-based parsers may still be faster; PicoSerDe's advantage is guaranteed deployability under trimming and self-contained publishing, not peak JIT throughput.

Benchmarks: AOT self-contained, .NET 10, 100K iterations, win-x64.

Module vs Competitor Avg Speedup Competitor AOT?
PicoJetson System.Text.Json 1.35x ✅
PicoMsgPack MessagePack-CSharp 1.40x ❌
PicoIni ini-parser 0.12x ❌
PicoToml Tommy 0.30x ❌
PicoYaml — — ❌

JSON/MessagePack are faster than or competitive with JIT-based alternatives even in AOT mode. INI/TOML/YAML prioritize correct, reflection-free parsing over peak throughput — their JIT competitors benefit from years of runtime-level optimizations (cached keys, direct span writes, dynamic code gen) that are incompatible with NativeAOT. PicoSerDe is the only option that runs at all in a fully-trimmed, self-contained NativeAOT deployment for these formats.


Design

// One API across all formats
JsonSerializer.Serialize<T>(value)      // → byte[] via PicoJetson
MsgPackSerializer.Deserialize<T>(data)  // T ← byte[] via PicoMsgPack
IniSerializer.Serialize(config)         // → string via PicoIni

Attribute-Driven Registration

PicoSerDe source generators discover types through four independent pipelines:

  1. Usage-driven — calling Serialize<T>() or Deserialize<T>() triggers generation for T
  2. Generic attribute — [PicoSerializable] marks a type for all referenced format modules
  3. Format-specific attribute — [PicoJsonSerializable] / [PicoIniSerializable] / etc. marks a type for one format
  4. Shorthand attribute — [GenerateSerializer(typeof(T))] for central registration
// All referenced formats generate serializers
[PicoSerializable]
public class UserDto { public string Name { get; set; } }

// JSON only (PicoJetson)
[PicoJsonSerializable]
public class JsonOnlyDto { public string Label { get; set; } }

// Indirect — target type from any assembly
[PicoIniSerializable(typeof(ExternalLibrary.SharedDto))]
class Config { }

// Shorthand — equivalent to PicoSerializable(typeof(T))
[GenerateSerializer(typeof(UserDto))]
[GenerateSerializer(typeof(ProductDto))]
class PicoSerDeConfig { }
Attribute Scope Defined in
[PicoSerializable] All formats — direct or typeof(T) PicoSerDe.Core
[GenerateSerializer] Shorthand for PicoSerializable(typeof(T)) PicoSerDe.Core
[PicoJsonSerializable] JSON only PicoJetson
[PicoIniSerializable] INI only PicoIni
[PicoTomlSerializable] TOML only PicoToml
[PicoMsgPackSerializable] MsgPack only PicoMsgPack
[PicoYamlSerializable] YAML only PicoYaml

No attributes are required for basic usage — calling Serialize<T>() automatically triggers generation.

Key Features

Polymorphic Deserialization (Type Discriminator)

Base types declare derived types at compile time. Zero reflection, AOT-safe. Since v2026.3.0.

[PicoSerializable]
[PicoDerivedType(typeof(MessageEntry), "message")]
[PicoDerivedType(typeof(CompactionEntry), "compaction")]
abstract class SessionEntry { }

class MessageEntry : SessionEntry { public string Content { get; set; } = string.Empty; }
class CompactionEntry : SessionEntry { public int From { get; set; } }

var json = """{"$type":"message","Content":"hello"}"""u8;
var result = JsonSerializer.Deserialize<SessionEntry>(json);
// result is MessageEntry at runtime
Feature Support
Serialization + Deserialization ✅ v2026.3.0
Streaming (PipeReader) ✅ v2026.3.2
Base class properties ✅ v2026.3.3
[JsonConstructor] on derived types ✅
Record derived types ✅ v2026.3.23
Complex/collection ctor params ✅ v2026.3.24
TOML / YAML poly support ✅ v2026.3.24
INI / MsgPack poly support ✅ v2026.4.0

DOM Layer (PicoDocument / PicoElement)

Schema-less JSON inspection without System.Text.Json. Zero-copy.

var doc = PicoDocument.Parse("""{"name":"Alice","age":30}"""u8.ToArray());

var name = doc.RootElement["name"].GetString();         // "Alice"
var ok   = doc.RootElement.TryGetProperty("age", out _); // true
bool valid = PicoDocument.IsValid("{}"u8);               // true

// Numeric access
long big = doc.RootElement["count"].GetInt64();
double d = doc.RootElement["score"].GetDouble();
if (doc.RootElement["age"].TryGetInt32(out int age))
    Console.WriteLine(age);

C# Records

record and record struct types are fully supported. Primary constructor auto-detected — no [JsonConstructor] needed. init-only properties work correctly.

Top-Level Arrays

Serialize<T[]>(...) / Deserialize<T[]>(...) and streaming DeserializeFromStreamAsync<T[]>(stream) work directly.

Three-Layer Test Structure

PicoJetson tests are split into Unit / Integration / Functional projects with clear boundaries.

No non-generic Serialize(Type, object?) overloads. PicoSerDe is designed for AOT-first usage where all types are known at compile time. SerRegistry<TFormat, T> static fields (PicoSerDe.Core) are shared across assemblies and provide faster lookup than a ConcurrentDictionary<Type, ...>. Framework wrappers should call the generic API internally — the type's serializer is guaranteed to be registered via ModuleInitializer as long as the type was discovered by any pipeline (usage-driven, attribute, or shorthand).

┌──────────────────────────────────────────────┐
│                 User Code                     │
└──────────────────┬───────────────────────────┘
                   │  Static SerRegistry<TFormat, T>
┌──────────────────▼───────────────────────────┐
│           PicoSerDe.Core                      │
│  ISerializer<T>  │  IDeserializer<T>         │
│  SerRegistry     │  DesRegistry              │
│  TokenType       │  SimdHelpers (Vector128)  │
│  TextHelpers     │  SerializerExtensions     │
└────┬────────┬─────────┬─────────┬─────────┬──┘
     │        │         │         │         │
 PicoJetson  PicoIni  PicoMsgPack PicoToml PicoYaml
   ││       ││         ││        ││       ││
  .Gen     .Gen       .Gen      .Gen     .Gen
  • Dual-package: each format → runtime library (net10.0) + source generator (netstandard2.0)
  • ref struct readers/writers — stack-allocated, zero heap allocation on hot path
  • Static SerRegistry<TFormat, T> — per-format registries in PicoSerDe.Core; JIT/AOT inlineable, no dictionary lookups
  • file struct generated implementations — devirtualization without sealed class overhead
  • Ref struct serialization — ref struct types are supported as serializable types across all 5 formats. Source-generator-generated static methods + delegate dispatch bypass the ISerializer<T> interface constraint.
  • JsonOptions — runtime configuration (indentation, naming policy, ignore conditions, etc.) flowing through ThreadStatic to SG-generated code
  • Polymorphic deserialization — type discriminator dispatch via [PicoDerivedType]; serialization + deserialization + streaming (v2026.3.0); record types (v2026.3.23); TOML/YAML poly (v2026.3.24); INI/MsgPack poly (v2026.4.0)
  • Anonymous type serialization — Serialize(new { A = 1, B = "x" }) with nested types, collections, PropertyNamingPolicy.CamelCase, DefaultIgnoreCondition.WhenWritingNull, and MaxDepth enforcement. Works across all 5 formats via C# 12 interceptors + unsafe field access (serialization only, C# 12+, <AllowUnsafeBlocks>true</AllowUnsafeBlocks>) (v2026.7.22)
  • PicoDocument / PicoElement — zero-copy JSON DOM for schema-less inspection (v2026.3.4)
  • C# records — primary constructor auto-detection, init-only support (v2026.3.3); poly+record (v2026.3.23); complex/collection ctor params (v2026.3.24)
  • Top-level arrays — Serialize<T[]>() / Deserialize<T[]>() with streaming (v2026.3.2)

PicoJetson JsonOptions

// Compact (default) — optimal for data transfer
byte[] data = JsonSerializer.SerializeToUtf8Bytes(model);

// Human-readable
byte[] data = JsonSerializer.SerializeToUtf8Bytes(model,
    new JsonOptions { Indented = true });

// CamelCase naming
byte[] data = JsonSerializer.SerializeToUtf8Bytes(model,
    new JsonOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });

// Skip null properties
byte[] data = JsonSerializer.SerializeToUtf8Bytes(model,
    new JsonOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull });

// Allow NaN/Infinity
byte[] data = JsonSerializer.SerializeToUtf8Bytes(model,
    new JsonOptions { NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals });

Available options:

Option Default Description
Indented false Human-readable indented output
MaxDepth 63 Maximum nesting depth
PropertyNamingPolicy null Naming policy: CamelCase, SnakeCaseLower, KebabCaseLower, PascalCase
DefaultIgnoreCondition Never Skip null/default properties: WhenWritingNull, WhenWritingDefault
NumberHandling Strict Allow named floats: AllowNamedFloatingPointLiterals
PropertyNameCaseInsensitive false Case-insensitive property matching (default is already case-insensitive)
AllowTrailingCommas false Accept trailing commas in objects/arrays
ReadCommentHandling Disallow Skip // and /* */ comments
UnmappedMemberHandling Skip Throw on unknown properties: Disallow
Null handling across formats

Every format's options class (JsonOptions, YamlOptions, TomlOptions, IniOptions, MsgPackOptions) exposes DefaultIgnoreCondition, but what "writing a null" means depends on the wire format:

Format Default (Never) WhenWritingNull
JSON "key":null written omitted
MsgPack nil written (map count adjusts automatically) omitted
TOML / INI omitted — these formats have no null literal omitted
YAML omitted — the reader has no null-literal support yet; writing key: would read back as a default value and break round-trip fidelity omitted

The matrix applies to every emit path — top-level members, nested objects, collection elements, nullable collections, and polymorphic dispatch — and is locked by cross-format regression tests (IgnoreConditionMatrixTests).

Per-property control is available via the cross-format [PicoIgnore] attribute (PicoSerDe.Core):

[PicoIgnore]                                                  // stripped everywhere (write + read)
public string Internal { get; set; } = "";

[PicoIgnore(Condition = PicoIgnoreCondition.WhenWritingNull)] // omitted only when null, regardless of global options
public string? Note { get; set; }

[PicoIgnore(Condition = PicoIgnoreCondition.Never)]           // exempt from the global DefaultIgnoreCondition
public string? Pinned { get; set; }

Conditions affect serialization only — deserialization still maps conditional properties. Format-specific markers ([JsonIgnore], [YamlIgnore], …) remain single-format unconditional ignores.

Custom serializers for nested types

Register applies at the top level only. To also override T wherever it appears as a nested value (object property, list element, dictionary value), use RegisterCustom — available on JSON and MessagePack:

JsonSerializer.RegisterCustom(new MySerializer(), new MyDeserializer());
// Outer { Foo Inner } now serializes Inner with MySerializer too.
// Deserialization override applies at the top level only.

Packages

Package NuGet
PicoSerDe.Core NuGet
PicoJetson / .Gen NuGet
PicoMsgPack / .Gen NuGet
PicoIni / .Gen NuGet
PicoToml / .Gen NuGet
PicoYaml / .Gen NuGet

CI/CD

Target Runner
win-x64 windows-latest
win-arm64 windows-latest
linux-x64 ubuntu-latest
linux-arm64 ubuntu-24.04-arm
osx-arm64 macos-latest

Every push: build + test (500+ tests) + 5 benchmarks smoke + 5 AOT sample publishes. Release: v* tag → packs 11 packages in dependency order → NuGet.org.


Comparison

PicoSerDe S.T.Json YamlDotNet VYaml MsgPack-CS Tommy
Formats 5 1 1 1 1 1
AOT ✅ ✅ ❌ ⚠️ ❌ ❌
Zero-reflection ✅ ✅ ❌ ❌ ❌ ❌
Zero annotations ✅ ✅ ✅ ❌ ❌ ✅
ref struct readers ✅ ✅ ❌ ❌ ❌ ❌
SIMD ✅ ❌ ❌ ❌ ❌ ❌
JSON DOM ✅ ✅ ❌ ❌ ❌ ❌
Polymorphic ✅ ✅ ❌ ❌ ❌ ❌

License

MIT

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2026.10.0 91 9/18/2026
2026.9.14 86 9/14/2026
2026.5.3 89 9/11/2026
2026.5.2 89 9/11/2026
2026.5.1 94 9/9/2026
2026.5.0 110 8/24/2026
2026.4.11 114 8/23/2026
2026.4.10 103 8/23/2026
2026.4.9 104 8/23/2026
2026.4.8 101 8/23/2026
2026.4.7 125 8/16/2026
2026.4.6 101 8/13/2026
2026.4.5 110 8/13/2026
2026.4.4 105 8/11/2026
2026.4.3 97 8/11/2026
2026.4.2 118 7/22/2026
2026.4.1 116 7/22/2026
2026.4.0 117 7/18/2026
2026.3.25 118 7/13/2026
2026.3.24 120 7/13/2026
Loading failed