TOML

edit

Serialize an ID

When the project references Meziantou.Framework.Toml, the generator adds a TOML converter to each ID and registers it with [TomlConverter]. It writes the value of the ID:

public sealed record OrderLine(ProductId ProductId, ProjectId ProjectId, int Quantity);
var line = new OrderLine(ProductId.FromGuid(new Guid("0f8fad5b-d9cb-469f-a165-70867728950e")), ProjectId.FromInt32(42), Quantity: 3);

var toml = Meziantou.Framework.Toml.TomlSerializer.Serialize(line);
Assert.Equal("""
    ProductId = "0f8fad5b-d9cb-469f-a165-70867728950e"
    ProjectId = 42
    Quantity = 3

    """, toml);

var deserialized = Meziantou.Framework.Toml.TomlSerializer.Deserialize<OrderLine>(toml);
Assert.Equal(line, deserialized);

How values are written

The TOML type of the value depends on the underlying type:

Underlying typeTOML value
boolA boolean.
byte, sbyte, short, ushort, int, uint, longAn integer. Reading a value out of the range of the type throws a TomlException.
ulong, Int128, UInt128, BigIntegerAn integer when the value fits in a TOML integer (a signed 64-bit integer), a string otherwise. Both are read.
Half, float, doubleA float. Integers are read too.
decimalA float with all the digits of the value. Integers are read too.
string, Guid, ObjectIdA string.
DateTimeAn offset date-time with Z for a UTC value, with the offset of the machine for a local value, and a local date-time otherwise. A Z offset is read as a UTC value, another offset as a local value, and a local date-time or a local date as an unspecified value.
DateTimeOffsetAn offset date-time. A local date-time, date or time cannot be read.

TOML has no null value. The serializer omits a property whose value is null, and the converter throws a TomlException when it is asked to write a null ID or an ID whose string value is null.

The keys of a TOML dictionary are strings: an ID cannot be the key of a dictionary.

Source generation and Native AOT

A source-generated TomlSerializerContext does not see the [TomlConverter] attribute that this generator adds to the ID: the TOML source generator cannot see the code of other source generators.

[Meziantou.Framework.Toml.Serialization.TomlSerializable(typeof(OrderLine))]
internal sealed partial class OrderTomlContext : Meziantou.Framework.Toml.Serialization.TomlSerializerContext;

Register the generated converters in options whose TypeInfoResolver is the context, and get the metadata of the type from these options:

// The generated converters are public: register them in options that use the context
var options = new Meziantou.Framework.Toml.TomlSerializerOptions
{
    TypeInfoResolver = OrderTomlContext.Default,
    Converters = [new ProductId.ProductIdTomlConverter(), new ProjectId.ProjectIdTomlConverter()],
};
var typeInfo = (Meziantou.Framework.Toml.TomlTypeInfo<OrderLine>)options.TypeInfoResolver.GetTypeInfo(typeof(OrderLine), options)!;

var line = new OrderLine(ProductId.FromGuid(new Guid("0f8fad5b-d9cb-469f-a165-70867728950e")), ProjectId.FromInt32(42), Quantity: 3);
var toml = Meziantou.Framework.Toml.TomlSerializer.Serialize(line, typeInfo);
Assert.Equal("""
    ProductId = "0f8fad5b-d9cb-469f-a165-70867728950e"
    ProjectId = 42
    Quantity = 3

    """, toml);

var deserialized = Meziantou.Framework.Toml.TomlSerializer.Deserialize(toml, typeInfo);
Assert.Equal(line, deserialized);

Add one converter per ID type that the serialized types contain.