Skip to content

xoofx/Tomlyn

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

382 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Tomlyn ci Coverage Status NuGet

Tomlyn is a high-performance .NET TOML 1.1 parser, round-trippable syntax tree, and System.Text.Json-style object serializer - NativeAOT ready.

SharpYaml Looking for YAML support? Check out SharpYaml.

Note: Tomlyn v1 is a major redesign with breaking changes from earlier versions. It uses a System.Text.Json-style API with TomlSerializer, TomlSerializerOptions, and resolver-based metadata (ITomlTypeInfoResolver). See the migration guide for details.

✨ Features

  • System.Text.Json-style API: familiar surface with TomlSerializer, TomlSerializerOptions, TomlTypeInfo<T>
  • TOML 1.1.0 only: Tomlyn v1 targets TOML 1.1.0 and does not support TOML 1.0
  • Source generation: NativeAOT / trimming friendly via TomlSerializerContext and [TomlSerializable] roots
  • Cross-project polymorphism: register derived types at runtime or on a source-generated context when base and derived types live in different assemblies
  • System.Text.Json attribute interop: reuse [JsonPropertyName], [JsonIgnore], [JsonRequired], [JsonConstructor], [JsonObjectCreationHandling], and polymorphism attributes
  • Flexible collection input: opt a collection member into accepting either a single TOML value or an array via [TomlSingleOrArray]
  • Allocation-free parsing pipeline: incremental TomlLexer β†’ TomlParser with precise spans for errors
  • Low-level access: full lexer/parser API plus a lossless, trivia-preserving syntax tree (SyntaxParser β†’ DocumentSyntax)
  • Reflection control: reflection-based POCO mapping is available, but can be disabled for NativeAOT via a feature switch / MSBuild property

πŸ“ Requirements

Tomlyn targets net8.0, net10.0, and netstandard2.0.

  • Consuming the NuGet package works on any runtime that supports netstandard2.0 (including .NET Framework) or modern .NET (net8.0+).
  • Building Tomlyn from source requires the .NET 10 SDK.

πŸ“¦ Install

dotnet add package Tomlyn

Tomlyn ships the source generator in-package (analyzers/dotnet/cs) - no extra package needed.

πŸš€ Quick Start

using Tomlyn;

// Serialize
var toml = TomlSerializer.Serialize(new { Name = "Ada", Age = 37 });

// Deserialize
var person = TomlSerializer.Deserialize<Person>(toml);

Untyped model (TomlTable)

using Tomlyn;
using Tomlyn.Model;

var toml = @"global = ""this is a string""
# This is a comment of a table
[my_table]
key = 1 # Comment a key
value = true
list = [4, 5, 6]
";

var model = TomlSerializer.Deserialize<TomlTable>(toml)!;
var global = (string)model["global"]!;

Console.WriteLine(global);
Console.WriteLine(TomlSerializer.Serialize(model));

Options

using System.Text.Json;
using Tomlyn;

var options = new TomlSerializerOptions
{
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
    PreferredObjectCreationHandling = JsonObjectCreationHandling.Replace,
    WriteIndented = true,
    IndentSize = 4,
    MaxDepth = 64,
    DefaultIgnoreCondition = TomlIgnoreCondition.WhenWritingNull,
};

var toml = TomlSerializer.Serialize(config, options);
var model = TomlSerializer.Deserialize<MyConfig>(toml, options);

By default, PropertyNamingPolicy is null, meaning CLR member names are used as-is for TOML mapping keys (same default as System.Text.Json). MaxDepth = 0 uses the built-in default of 64. PreferredObjectCreationHandling = JsonObjectCreationHandling.Replace matches System.Text.Json: read-only properties are not populated unless you opt into Populate via options or [JsonObjectCreationHandling].

Source Generation

using System.Text.Json.Serialization;
using Tomlyn.Serialization;

public sealed class MyConfig
{
    public string? Global { get; set; }
}

[TomlSourceGenerationOptions(
    PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
    PreferredObjectCreationHandling = JsonObjectCreationHandling.Replace)]
[TomlSerializable(typeof(MyConfig))]
internal partial class MyTomlContext : TomlSerializerContext
{
}

var config = TomlSerializer.Deserialize(toml, MyTomlContext.Default.MyConfig);
var tomlOut = TomlSerializer.Serialize(config, MyTomlContext.Default.MyConfig);

Cross-Project Polymorphism

When a base type lives in one project and derived types live in another, you can register derived types without putting [TomlDerivedType] on the base type.

Reflection path:

using Tomlyn;

var options = new TomlSerializerOptions
{
    PolymorphismOptions = new TomlPolymorphismOptions
    {
        TypeDiscriminatorPropertyName = "kind",
        DerivedTypeMappings = new Dictionary<Type, IReadOnlyList<TomlDerivedType>>
        {
            [typeof(Animal)] =
            [
                new(typeof(Cat), "cat"),
                new(typeof(Dog), "dog"),
            ],
        },
    },
};

Source generation path:

using Tomlyn.Serialization;

[TomlSerializable(typeof(Animal))]
[TomlDerivedTypeMapping(typeof(Animal), typeof(Cat), "cat")]
[TomlDerivedTypeMapping(typeof(Animal), typeof(Dog), "dog")]
internal partial class MyTomlContext : TomlSerializerContext
{
}

Use TomlPolymorphismOptions.DerivedTypeMappings and [TomlDerivedTypeMapping] additively with existing base-type attributes. Base-type registrations still take precedence when the same discriminator or derived type is registered more than once.

Single Value Or Array Collections

using System.Text.Json.Serialization;
using Tomlyn.Serialization;

public sealed class PackagingConfiguration
{
    public PackagingConfiguration()
    {
        RuntimeIdentifiers = new List<string>();
    }

    [TomlSingleOrArray]
    [JsonPropertyName("rid")]
    public List<string> RuntimeIdentifiers { get; }
}

With [TomlSingleOrArray], both of these TOML payloads are valid:

rid = "win-x64"
rid = ["win-x64", "linux-x64"]

For mutable read-only collection members, Tomlyn appends into the existing collection instance. Without [TomlSingleOrArray], collection members still require a TOML array.

Reflection Control

Reflection fallback can be disabled globally before first serializer use:

AppContext.SetSwitch("Tomlyn.TomlSerializer.IsReflectionEnabledByDefault", false);

When publishing with NativeAOT (PublishAot=true), the Tomlyn NuGet package disables reflection-based serialization by default. You can override the default by setting the following MSBuild property in your app project:

<PropertyGroup>
  <TomlynIsReflectionEnabledByDefault>true</TomlynIsReflectionEnabledByDefault>
</PropertyGroup>

πŸ“– Documentation

πŸͺͺ License

This software is released under the BSD-Clause 2 license.

πŸ€— Author

Alexandre Mutel aka xoofx.

About

Tomlyn is a TOML parser, validator and authoring library for .NET Framework and .NET Core

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project

 

Contributors

Languages