Table of Contents

Bodu.Text.Toml

Bodu.Text.Toml

Bodu.Text.Toml is a library for TOML v1.0.0 and v1.1.0, the human-readable configuration format built around typed key/value pairs and tables. It is one of the three Bodu serializers - it shares the architecture described in the family introduction (the serializer / DOM / reader-writer tiers, converters, attributes, naming policies) with its sibling libraries Bodu.Text.Bencode and Bodu.Text.Yaml, so the family vocabulary applies here unchanged. This page covers what is specific to TOML.

The format in one paragraph

TOML is a text format aimed squarely at configuration files: obvious to read, typed without annotations, and table-structured. It has a rich native value model - strings, integers, floats (including inf / nan), Booleans, arrays, tables, arrays of tables, and the four RFC 3339 date-time forms (offset date-time, local date-time, local date, local time). The document root is always a table, so the type you serialize at the root must map to an object; a top-level scalar or array throws.

A rich native value model

Because TOML carries real scalar kinds, most everyday .NET types map without converters:

  • bool → boolean; double / float / Half → float; the integer family → integer (within the i64 range).
  • DateTimeOffset, DateTime, DateOnly, and TimeOnly map one-to-one onto the four RFC 3339 date-time forms - no string conventions to invent.
  • string, char, Guid, Uri, and Version → string; TimeSpan → the invariant "c"-format string.
  • decimal and byte[] have two representations each, selected on the options: TomlDecimalHandling chooses between a native (binary64-bounded) float and a lossless string, and TomlByteArrayHandling chooses between an integer array and a Base64 string.

TOML has no null, so a null member is omitted on write by default. The full per-type catalog lives in the type-mapping table and the built-in converter catalog.

Spec versions

TomlSpecVersion on the options selects the grammar the reader enforces. The default is strict v1.0.0; opting in to v1.1.0 additionally accepts:

  • the \e (escape, U+001B) and \xHH (two-hex-digit) string escapes;
  • time values written without seconds (09:30 rather than 09:30:00);
  • inline tables that span multiple lines and carry a trailing comma.

The version affects parsing only. The writer always emits output valid under both versions, so produced documents never lock consumers into the newer grammar - the SpecVersion property on the writer options is obsolete and ignored.

Diagnostics with positions

TOML files are edited by hand, so parse failures must point at the offending line. A malformed document raises TomlFormatException carrying the line, column, and byte offset (LineNumber, ColumnNumber, Offset); a document that parses but cannot bind to your type raises TomlSerializationException, which carries the same positions where known plus a Path naming the member that failed to bind.

Headline types

Type Purpose
TomlSerializer Serialize to string / IBufferWriter<byte> (UTF-8), or to a Stream via SerializeAsync; Deserialize<T> from string / ReadOnlySpan<byte> / Stream (with DeserializeAsync).
TomlSerializerOptions Converters, naming policy, ignore conditions, SpecVersion, ByteArrayHandling, DecimalHandling, depth.
NamingPolicy Property-name policy: CamelCase, SnakeCaseLower, SnakeCaseUpper, KebabCaseLower, KebabCaseUpper.
TomlConverter<T> Base class for a custom converter over the reader/writer pair; attach one to a member or type with ConverterAttribute. Built-in enum converters: TomlStringEnumConverter and TomlNumberEnumConverter<TEnum>.
TomlNode Mutable DOM - Parse, index, mutate, write back.
TomlDocument Read-only, low-allocation DOM walked through RootElement.
Utf8TomlReader / TomlDocumentReader / Utf8TomlWriter Forward-only, allocation-free ref struct token machines: the source-order lexer, the normalized structural cursor a converter receives, and the writer.
TomlFormatException / TomlSerializationException Malformed input (with line/column/offset) vs a value that cannot bind.

Common scenarios

You want to… Use
Load an application's .toml configuration into a typed record TomlSerializer.Deserialize<T>
Emit configuration a human will edit TomlSerializer.Serialize - canonical output in document order, [PropertyOrder] honored
Patch one value in an existing file without a model TomlNode - index, mutate, write back
Inspect a document with minimal allocation TomlDocument and RootElement
Accept TOML v1.1.0 input SpecVersion = TomlSpecVersion.V1_1 on the options
Keep decimal values lossless on the wire DecimalHandling = TomlDecimalHandling.String

Where to go next