Compile-time code generation for zero-allocation serialization. AOT-compatible, generates static Serialize/Deserialize methods. Perfect for hot paths and maximum performance.
$ dotnet add package ToonNet.SourceGeneratorsCompile-time code generation for zero-allocation TOON serialization
ToonNet.SourceGenerators uses C# Source Generators to generate serialization code at compile-time:
Perfect for:
# Core package (required)
dotnet add package ToonNet.Core
# Source generators
dotnet add package ToonNet.SourceGenerators
using ToonNet.Core.Serialization.Attributes;
// Mark class with [ToonSerializable]
[ToonSerializable]
public partial class Person
{
public string Name { get; set; }
public int Age { get; set; }
public List<string> Hobbies { get; set; }
}
// Use the class
var person = new Person
{
Name = "Alice",
Age = 28,
Hobbies = new List<string> { "Reading", "Coding" }
};
// Serialize using ToonSerializer (runtime)
string toon = ToonSerializer.Serialize(person);
// Deserialize using ToonSerializer (runtime)
var personBack = ToonSerializer.Deserialize<Person>(toon);
// Alternative: Use generated static methods directly
var doc = Person.Serialize(person);
var restored = Person.Deserialize(doc);
Note: Source generator creates static Serialize and Deserialize methods at compile-time. You can use them directly or through ToonSerializer for zero-allocation performance.
Your Code:
[ToonSerializable]
public partial class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
Generated Code (simplified):
// Source generator creates static Serialize and Deserialize methods
public partial class Product
{
/// <summary>
/// Serializes this instance to a TOON document (generated code).
/// </summary>
public static ToonDocument Serialize(
Product value,
ToonSerializerOptions? options = null)
{
// Direct property access with no reflection overhead
var obj = new ToonObject();
obj["Id"] = new ToonNumber(value.Id);
obj["Name"] = new ToonString(value.Name);
obj["Price"] = new ToonNumber((double)value.Price);
return new ToonDocument(obj);
}
/// <summary>
/// Deserializes a TOON document to an instance (generated code).
/// </summary>
public static Product Deserialize(
ToonDocument doc,
ToonSerializerOptions? options = null)
{
var obj = (ToonObject)doc.Root;
var result = new Product();
result.Id = (int)((ToonNumber)obj["Id"]).Value;
result.Name = ((ToonString)obj["Name"]).Value;
result.Price = (decimal)((ToonNumber)obj["Price"]).Value;
return result;
}
}
Marks a class for source generation:
[ToonSerializable]
public partial class MyClass
{
// Class must be partial
// Must have parameterless constructor (or primary constructor)
}
Customizes property serialization:
[ToonSerializable]
public partial class Product
{
[ToonProperty("product_id")]
public int Id { get; set; }
public string Name { get; set; }
}
// Serializes as:
// product_id: 123
// Name: Laptop
Excludes properties from serialization:
[ToonSerializable]
public partial class User
{
public string Username { get; set; }
[ToonIgnore]
public string PasswordHash { get; set; } // Not serialized
}
BenchmarkDotNet v0.13.12, Windows 11
Intel Core i7-12700K, 1 CPU, 12 logical and 8 physical cores
.NET SDK 8.0.100
| Method | Mean | Allocated |
|-------------------------- |----------:|----------:|
| SourceGenerator_Serialize | 45.2 ns | - | ← Zero allocation!
| ExpressionTree_Serialize | 89.5 ns | 120 B |
| Reflection_Serialize | 4,250 ns | 856 B |
Source Generator is:
- 2x faster than Expression Trees
- 94x faster than Reflection
- Zero heap allocations (hot path)
✅ Use Source Generators When:
⚠️ Use Runtime Serialization When:
using ToonNet.Core.Serialization;
using ToonNet.Core.Serialization.Attributes;
[ToonSerializable]
public partial class ApiResponse
{
public int StatusCode { get; set; }
public string Message { get; set; }
public DateTime Timestamp { get; set; }
}
[ApiController]
[Route("api/[controller]")]
public class DataController : ControllerBase
{
[HttpGet]
public IActionResult GetData()
{
var response = new ApiResponse
{
StatusCode = 200,
Message = "Success",
Timestamp = DateTime.UtcNow
};
// Zero-allocation serialization (source generator optimized)
var toon = ToonSerializer.Serialize(response);
return Content(toon, "application/toon");
}
}
using ToonNet.Core.Serialization;
using ToonNet.Core.Serialization.Attributes;
[ToonSerializable]
public partial class PlayerState
{
public int PlayerId { get; set; }
public Vector3 Position { get; set; }
public float Health { get; set; }
public int Score { get; set; }
}
public class NetworkManager
{
public void BroadcastState(PlayerState state)
{
// Critical path - zero allocations
string data = ToonSerializer.Serialize(state);
networkSocket.Send(data);
}
public PlayerState ReceiveState(string data)
{
// Fast deserialization
return ToonSerializer.Deserialize<PlayerState>(data);
}
}
using ToonNet.Core.Serialization;
using ToonNet.Core.Serialization.Attributes;
[ToonSerializable]
public partial class SensorReading
{
public DateTime Timestamp { get; set; }
public double Temperature { get; set; }
public double Humidity { get; set; }
public int BatteryLevel { get; set; }
}
public class SensorDevice
{
public void SendTelemetry()
{
var reading = new SensorReading
{
Timestamp = DateTime.UtcNow,
Temperature = ReadTemperature(),
Humidity = ReadHumidity(),
BatteryLevel = GetBatteryLevel()
};
// Minimal memory footprint
var payload = ToonSerializer.Serialize(reading);
mqttClient.Publish("sensors/data", payload);
}
}
// Project file configuration
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<PublishAot>true</PublishAot> <!-- Enable Native AOT -->
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ToonNet.Core" />
<PackageReference Include="ToonNet.SourceGenerators" />
</ItemGroup>
</Project>
// Code
using ToonNet.Core.Serialization;
using ToonNet.Core.Serialization.Attributes;
[ToonSerializable]
public partial class Config
{
public string AppName { get; set; }
public int MaxConnections { get; set; }
}
// Works with Native AOT (no reflection!)
var config = new Config { AppName = "MyApp", MaxConnections = 100 };
var toon = ToonSerializer.Serialize(config);
string, int, long, short, byte, sbyteuint, ulong, ushortfloat, double, decimalbool, char, Guid, DateTime, DateTimeOffsetList<T>, T[]Dictionary<TKey, TValue>IEnumerable<T>, IList<T>, ICollection<T>[ToonSerializable]int?, DateTime?)❌ Not Supported:
ToonSerializer methods are safe to call concurrently across threads.ConcurrentDictionary for concurrent access.ToonSerializerOptions instance concurrently across threads.# Run source generator tests
cd tests/ToonNet.SourceGenerators.Tests
dotnet test
# Verify generated code
dotnet build --verbosity detailed
# Check obj/Debug/net8.0/generated/ folder
Core:
ToonNet.Core - Core serialization (required)Extensions:
ToonNet.Extensions.Json - JSON ↔ TOONToonNet.Extensions.Yaml - YAML ↔ TOONWeb:
ToonNet.AspNetCore - ASP.NET Core DIToonNet.AspNetCore.Mvc - MVC formattersDevelopment:
ToonNet.Benchmarks - Performance testsToonNet.Tests - Test suiteMIT License - See LICENSE file for details.
Contributions welcome! Please read CONTRIBUTING.md first.
Part of the ToonNet serialization library family.