.NET implementation of Token-Oriented Object Notation (TOON), aligned with the https://github.com/toon-format/toon specification, providing a consistent API experience and options model similar to System.Text.Json.
- High-performance encoding: Object, inline atomic arrays, tabular object arrays, and other paths are implemented
- Decoding pipeline: Scanning/parsing/validation is in progress, currently supports atomic value reading
- Design follows "minimal allocation, readability first" engineering trade-offs
C#.ToonSerializer · C#.ToonSerializerOptions · C#.Encoders.EncodeValue() · C#.Primitives.EncodePrimitive() · C#.LineWriter
- NuGet: AIDotNet.Toon
- Target Frameworks: net8.0 / net9.0 / net10.0 (see src/AIDotNet.Toon/AIDotNet.Toon.csproj)
- License: MIT
NuGet:
dotnet add package AIDotNet.ToonSource code method:
- Add the
src/AIDotNet.Toondirectory to your solution, or include it as a submodule - Enable package Readme in your csproj (already configured in this project): see src/AIDotNet.Toon/AIDotNet.Toon.csproj
Serialize to TOON:
using Toon;
var options = new ToonSerializerOptions
{
Indent = 2,
Delimiter = ToonDelimiter.Comma,
Strict = true,
LengthMarker = null
};
var data = new
{
users = new[]
{
new { name = "alice", age = 30 },
new { name = "bob", age = 25 }
},
tags = new[] { "a", "b", "c" },
numbers = new[] { 1, 2, 3 }
};
string toonText = ToonSerializer.Serialize(data, options);
// users[2]{name,age}:
// 1,alice
// 2,bob
// tags[3]: a,b,c
// numbers[3]: 1,2,3Deserialize from TOON to .NET (currently only atomic values are stable, other structures will improve as the decoder is enhanced):
using Toon;
var s = ToonSerializer.Deserialize<string>("hello", options); // "hello"
var n = ToonSerializer.Deserialize<double>("3.1415", options); // 3.1415Related APIs: C#.ToonSerializer.Serialize() · C#.ToonSerializer.Deserialize()
Public API:
- Generic serialization: C#.ToonSerializer.Serialize()
- Explicit type serialization: C#.ToonSerializer.Serialize()
- Generic deserialization: C#.ToonSerializer.Deserialize()
- Explicit type deserialization: C#.ToonSerializer.Deserialize()
- Byte APIs:
- Stream APIs:
- Write to stream: C#.ToonSerializer.Serialize() · C#.ToonSerializer.Serialize()
- Decode from stream: C#.ToonSerializer.Deserialize() · C#.ToonSerializer.Deserialize()
Options model: C#.ToonSerializerOptions
- Indent: Number of spaces per indentation level, default 2
- Delimiter: Delimiter, enumeration C#.ToonDelimiter (Comma / Tab / Pipe)
- Strict: Strict mode for decoding (indentation/blank lines/extra items validation), default true
- LengthMarker: Array length marker, only supports
#or null, default null - JsonOptions: Pass-through to System.Text.Json's C#.JsonSerializerOptions, defaults to enabling named float literals and registers converters that write
NaN/±Infinityasnull: C#.DoubleNamedFloatToNullConverter / C#.SingleNamedFloatToNullConverter
Default instance: C#.ToonSerializerOptions.Default
Entry point: C#.Encoders.EncodeValue()
Objects:
- Key-by-key output: C#.Encoders.EncodeObject() and C#.Encoders.EncodeKeyValuePair()
- Key names: Write bare if safe, otherwise quote: C#.Primitives.EncodeKey()
- Empty objects: Only output
key:
Atomic arrays (inline):
- Header: C#.Primitives.FormatHeader()
- Inline concatenation: C#.Encoders.EncodeInlineArrayLine()
Object arrays (tabular):
- Extract table header: C#.Encoders.ExtractTabularHeader()
- Header and data row output: C#.Encoders.EncodeArrayOfObjectsAsTabular() · C#.Encoders.WriteTabularRows()
Mixed/complex arrays (fallback to list):
- C#.Encoders.EncodeMixedArrayAsListItems()
- List item writer: C#.LineWriter.PushListItem()
Atoms/strings:
- Atomic encoding: C#.Primitives.EncodePrimitive()
- String quoting rules: C#.Primitives.EncodeStringLiteral()
- Number rules:
NaN/±Infinity -> null,-0 -> 0, others use C#.JsonElement.GetRawText()
Lines and indentation:
- Writer: C#.LineWriter
- Regular line: C#.LineWriter.Push()
- List item line: C#.LineWriter.PushListItem()
Simple object:
var obj = new { a = 1, b = "x" };
var toon = ToonSerializer.Serialize(obj);
// a: 1
// b: xAtomic array (default comma-separated):
var arr = new[] { 1, 2, 3 };
ToonSerializer.Serialize(arr); // "[3]: 1,2,3"Object array (tabular):
var rows = new[] { new { id = 1, name = "alice" }, new { id = 2, name = "bob" } };
ToonSerializer.Serialize(rows);
// [2]{id,name}:
// 1,alice
// 2,bobBytes and streams:
// Bytes
var bytes = ToonSerializer.SerializeToUtf8Bytes(rows);
var rowsFromBytes = ToonSerializer.Deserialize<List<Dictionary<string, object>>>(bytes);
// Stream
using var ms = new MemoryStream();
ToonSerializer.Serialize(rows, ms); // Write UTF-8 (no BOM), keep stream open
ms.Position = 0;
var rowsFromStream = ToonSerializer.Deserialize<List<Dictionary<string, object>>>(ms);Special number handling:
ToonSerializer.Serialize(new { v = double.NaN }); // "v: null"
ToonSerializer.Serialize(new { v = double.PositiveInfinity }); // "v: null"
ToonSerializer.Serialize(new { v = BitConverter.Int64BitsToDouble(unchecked((long)0x8000000000000000)) }); // "v: 0"Related assertions can be seen in tests: tests/AIDotNet.Toon.Tests/EncodeTests.cs
This implementation minimizes allocations and unnecessary branches while maintaining output readability:
- Indentation cache: C#.LineWriter.Push() maintains
_indentCacheto avoid repeated construction - No LINQ: Table header detection/data row writing paths use plain enumerator traversal: C#.Encoders.ExtractTabularHeader() · C#.Encoders.WriteTabularRows()
- Reuse concatenation: Atomic arrays and table rows use segment-by-segment Append: C#.Primitives.EncodeAndJoinPrimitives() · C#.Encoders.WriteTabularRows()
- Raw numbers: Prioritize C#.JsonElement.GetRawText() to ensure precision; fast path normalizes
-0
Publishing recommendations: Use Release build; consider R2R/ReadyToRun to improve startup performance.
- Syntax/rendering rules reference the upstream specification and implementation:
- Specification and reference implementation: https://github.com/toon-format/toon
- .NET version module mapping:
- Encoder: C#.ToonEncoder.Encode() -> C#.Encoders.EncodeValue()
- Decoder: C#.ToonDecoder.DecodeToJsonString() (placeholder, gradually improving)
- Runtime: .NET 8/9/10
- Platforms: Windows / Linux / macOS
- Package metadata and Readme integration: see src/AIDotNet.Toon/AIDotNet.Toon.csproj
- Decoding: Scanner / Parser / Validation / Decoders
- Strict mode error model: Provide row/column and context-aware C#.ToonFormatException
- Normalization strategy (Normalize): Cross-language consistency for dates/collections, etc.
- Documentation and example improvements, publish NuGet package
- Run tests:
dotnet test- Local packaging:
dotnet pack -c Release- Important source files:
Issues and PRs are welcome. Please try to:
- Keep the public API consistent with the System.Text.Json style
- Prioritize readability/real benefits when optimizing
- Add unit tests for new paths and edge conditions
MIT © AIDotNet.Toon Contributors
Acknowledgments: Thanks to the upstream project https://github.com/toon-format/toon for design and implementation reference.