Tessera.Serialization
0.1.1-preview
dotnet add package Tessera.Serialization --version 0.1.1-preview
NuGet\Install-Package Tessera.Serialization -Version 0.1.1-preview
<PackageReference Include="Tessera.Serialization" Version="0.1.1-preview" />
<PackageVersion Include="Tessera.Serialization" Version="0.1.1-preview" />
<PackageReference Include="Tessera.Serialization" />
paket add Tessera.Serialization --version 0.1.1-preview
#r "nuget: Tessera.Serialization, 0.1.1-preview"
#:package Tessera.Serialization@0.1.1-preview
#addin nuget:?package=Tessera.Serialization&version=0.1.1-preview&prerelease
#tool nuget:?package=Tessera.Serialization&version=0.1.1-preview&prerelease
Tessera.Serialization
Binary buffers from plain .NET objects, read in place by C++20 and .NET: no schema file, no parsing, and no allocations on the read side. This package holds the .NET library, the source generator that writes a serializer for each of your classes, the tool that writes their C++ headers after each build, and the header-only C++20 library those headers use.
Example
Your C# classes are the schema:
namespace Game;
public class Monster
{
public string? Name;
public short Hp = 100; // equal to its default: not stored
public int? Mana; // nullable: C++ can tell whether it was set
public Vec3 Position; // plain struct: stored inline
public List<Weapon>? Weapons;
}
public class Weapon { public string? Name; public int Damage; }
public record struct Vec3(float X, float Y, float Z);
Save one. That call is all the source generator needs: it writes a serializer for Monster and for every type
Monster uses.
using Game;
using Tessera;
var orc = new Monster
{
Name = "Orc",
Mana = 25,
Position = new Vec3(1, 2, 3),
Weapons = [new() { Name = "Axe", Damage = 9 }, new() { Name = "Bow", Damage = 4 }],
};
byte[] buffer = TesseraSerializer.Serialize(orc);
File.WriteAllBytes("monster.bin", buffer);
Console.WriteLine($"{buffer.Length} bytes");
328 bytes
The same build writes a C++ header for each class. C++ verifies the buffer once, then reads it where it lies:
#include "game/Monster.tessera.hpp"
// data, size: the 328 bytes of monster.bin, in 8-byte aligned memory
tessera::Reader<game::Monster> reader(data, size); // verifies the buffer once
if (!reader) return 1;
game::Monster orc = reader.root(); // a view: reads in place, nothing is parsed or copied
const game::Vec3& p = orc.position();
std::cout << orc.name() << ": hp " << orc.hp() << ", mana " << orc.mana().value_or(0)
<< ", position " << p.x << ' ' << p.y << ' ' << p.z << '\n';
for (game::Weapon w : orc.weapons()) std::cout << " " << w.name() << ": damage " << w.damage() << '\n';
Orc: hp 100, mana 25, position 1 2 3
Axe: damage 9
Bow: damage 4
.NET reads the buffer in place too, through a read type generated for each class (MonsterView for Monster), or
creates the objects again:
MonsterView view = MonsterView.Read(buffer); // checks the buffer once; copies nothing
Vec3 p = view.Position;
Console.WriteLine($"{view.Name}: hp {view.Hp}, mana {view.Mana}, position {p.X} {p.Y} {p.Z}");
foreach (WeaponView w in view.Weapons) Console.WriteLine($" {w.Name}: damage {w.Damage}");
Monster copy = TesseraSerializer.Deserialize<Monster>(buffer);
Console.WriteLine($"{copy.Name} again, with {copy.Weapons!.Count} weapons");
Orc: hp 100, mana 25, position 1 2 3
Axe: damage 9
Bow: damage 4
Orc again, with 2 weapons
Setup
dotnet add package Tessera.Serialization --prerelease
Then choose where the C++ headers go, in your project file:
<PropertyGroup>
<TesseraCppOutputDir>cpp/generated</TesseraCppOutputDir>
</PropertyGroup>
After each build, that folder holds a header for each class and the C++ runtime headers: add it to your C++ include
path. Buffers must start at an 8-byte aligned address, as memory from new, malloc or a std::vector<std::uint64_t>
does. Without TesseraCppOutputDir, no headers are written to disk.
Why Tessera
- Your classes are the schema. No separate schema file and no required attributes: classes, records, structs, enums, nullable values, lists, dictionaries, and fields declared as an abstract class or interface, which can hold any derived class. Types that can't be saved are reported at compile time, with a suggested replacement.
- No parsing. C++ and .NET validate a buffer once; after that, every field read is a plain memory load: nothing is parsed, copied or allocated.
- Versioning by name. Add, remove, reorder and rename fields, with no field numbers to manage: older and newer programs read each other's data.
- Compact. Fields that are null or equal to their default take no space, and equal strings are stored once.
- Measured. Benchmarked against FlatBuffers, MessagePack, Protocol Buffers, JSON and MemoryPack, in .NET and C++: see the results.
Documentation
- README and the documentation
- Installation and Getting started
- Supported types, attributes and options
- Reading in C++ and reading in .NET
- Changing your classes: which changes keep data saved earlier readable
- Compiler messages: every
TESSERAerror and warning, and how to fix it - Wire format and benchmarks
Tessera is a preview: the API and the wire format may change before 1.0. It is released under the Apache License 2.0.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. 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. |
-
net8.0
- No dependencies.
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 |
|---|---|---|
| 0.1.1-preview | 42 | 10/17/2026 |
| 0.1.0-preview | 46 | 10/17/2026 |