Kuddle.Net is a .NET implementation of a KDL parser/serializer targeting v2 of the spec. KDL is a concise, human-readable language built for configuration and data exchange. Head to https://kdl.dev for more specifics on the KDL document language itself.
Implement KDL v2 serialization in a .NET project.
Run the installation command in your project directory:
dotnet add package Kuddle.NetCreate a class with a parameterless constructor. Kuddle.Net uses kebab-case for KDL node names by default.
using Kuddle.Serialization;
public class Plugin
{
public string Name { get; set; } = string.Empty;
public string Version { get; set; } = "1.0.0";
}Use the KdlSerializer static class for string-based operations.
using Kuddle.Serialization;
// Initialize data
var plugin = new Plugin { Name = "Kuddle", Version = "2.0.0" };
// 1. Convert Object to KDL String
string kdl = KdlSerializer.Serialize(plugin);
// Result: plugin name="Kuddle" version="2.0.0"
// 2. Convert KDL String back to Object
var result = KdlSerializer.Deserialize<Plugin>(kdl);KDL utilizes a node-based hierarchy. Use the following table to map KDL concepts to .NET types.
| KDL Concept | .NET Equivalent | Example |
|---|---|---|
| Node | Class / POCO | server { ... } |
| Argument | Positional Value | node "value" |
| Property | Key-Value Pair | node key="value" |
| Children | Nested Objects/Collections | node { child_node } |
| Annotation | Type metadata | (uuid)"..." |
Control how C# properties map to KDL structures using attributes.
Kuddle.Net mandates kebab-case for implicit names. A property SystemSettings maps to node or key system-settings.
Override naming by passing a string argument to mapping attributes:
[KdlProperty("serial_NO")]
public string SerialNumber { get; set; }KDL nodes store data in three slots. Use attributes to assign properties to specific slots:
| Attribute | KDL Target | Mapping Logic |
|---|---|---|
| [KdlProperty] | Property | Key-value pairs: key="value". |
| [KdlArgument] | Argument | Positional values: node "value". |
| [KdlNode] | Child Node | Nested nodes or blocks: node { child }. |
Default Inference:
- Scalars (int, string, bool, DateTime): Maps to Properties.
- Complex Types / Collections: Maps to Child Nodes.
Specify the 0-based index for positional values.
public record User(
[property: KdlArgument(0)] int Id,
[property: KdlArgument(1)] string Role
);
// Output: user 1 "admin"The "Rest" Argument Constraint: Map a collection to an argument to capture all remaining values.
- Requirement: The collection argument must possess the highest index in the class.
- Uniqueness: Only one collection argument is permitted per node.
Kuddle.Net follows KDL v2 strict type requirements for booleans and nulls.
Null Fidelity:
Toggle IgnoreNullValues in KdlSerializerOptions:
- True (Default): Omit null properties from output.
- False: Emit the
#nullliteral.
Boolean Explicitness:
KDL requires #true or #false. Bare identifiers like true are parsed as KdlString, not KdlBool. Kuddle.Net handles this conversion automatically for System.Boolean types.
Manage complex document hierarchies through collection strategies, and unmapped data capture.
Kuddle.Net provides two strategies for mapping IEnumerable<T> properties.
Wrapped Collections (Default): The property name defines a parent node, and items appear as children.
[KdlNode("items")]
public List<string> Tags { get; set; } = ["net10", "kdl"];
/* Output:
items {
- "net10"
- "kdl"
}
*/Flattened Collections:
Set Flatten = true to omit the container node and emit items as siblings.
[KdlNode("tag", Flatten = true)]
public List<string> Tags { get; set; } = ["net10"];
/* Output:
tag "net10"
*/Flatten complex objects to merge their properties into the parent node's scope.
public class Root {
[KdlNode(Flatten = true)]
public Metadata Info { get; set; }
}
public class Metadata {
[KdlProperty] public string Author { get; set; }
}
// Result: root author="name"Constraint: Flattening is restricted to collections and complex objects. Applying Flatten = true to a scalar type (e.g., int, string) throws KdlConfigurationException.
Use [KdlExtensionData] to preserve KDL elements that do not match existing class members.
Requirements:
- Property type must be
IDictionary<string, object>orIDictionary<string, KdlValue>. - Unmapped properties are stored as native CLR types (
string,double,bool). - Unmapped nodes are stored as raw
KdlNodeAST objects.
public class Config {
[KdlExtensionData]
public Dictionary<string, object> CatchAll { get; set; }
}Note: Elements prefixed with the slashdash /- are ignored by the parser and are not captured.
Set the RootMapping property in KdlSerializerOptions to define top-level structure.
| Strategy | Description | Best Use Case |
|---|---|---|
| AsNode (Default) | Maps the object to one root node. | Data exchange / Storage. |
| AsDocument | Maps properties to top-level nodes. | Config files (e.g., appsettings.kdl). |
Enforce type safety and data integrity using KDL v2 type annotations and reserved type validators.
Kuddle.Net automatically emits and resolves reserved KDL annotations for standard .NET types.
| .NET Type | KDL Annotation | Output Example |
|---|---|---|
| Guid | (uuid) |
(uuid)"550e...4000" |
| DateTimeOffset | (date-time) |
(date-time)"2023-10-05T14:48:00Z" |
| DateOnly | (date) |
(date)"2023-10-05" |
| TimeOnly | (time) |
(time)"14:48:00" |
| TimeSpan | (duration) |
(duration)"PT1H30M" |
Specify bit-widths for numeric entries using the TypeAnnotation property on mapping attributes. This ensures cross-platform compatibility for integer and floating-point types.
public class Metrics
{
[KdlProperty(TypeAnnotation = "u8")]
public byte Priority { get; set; }
[KdlProperty(TypeAnnotation = "f64")]
public double Velocity { get; set; }
}
// Output: priority=(u8)10 velocity=(f64)120.5Enable KdlReservedTypeValidator to ensure values for specific identifiers (e.g., ipv4, regex, base64) conform to their format specifications.
Enable/Disable Validation:
Modify KdlReaderOptions before parsing. Validation is enabled by default.
var options = new KdlReaderOptions { ValidateReservedTypes = true };
var doc = KdlReader.Read(kdlText, options);Handle Validation Failures:
Catch KuddleValidationException to inspect specific failures. This exception contains an Errors collection referencing the failing node and a descriptive message.
try {
KdlReader.Read(kdlText);
} catch (KuddleValidationException ex) {
foreach (var err in ex.Errors) Console.WriteLine(err.Message);
}Enums serialize as bare strings (unquoted identifiers).
- Serialization: Emits the exact member name string.
- Deserialization: Performs case-insensitive matching against member names.
public enum Status { Active, Inactive }
public class Account {
public Status State { get; set; }
}
// KDL: account state=ActiveManage KDL output structure and string representation via KdlWriterOptions and KdlStringStyle.
Kuddle.Net selects string formats based on character content and configured flags.
| Style | Result | Selection Criteria |
|---|---|---|
| Bare | name |
No spaces or reserved characters ()[]{}/\"#;=. Cannot start with a digit. |
| Quoted | "name" |
Contains spaces or reserved characters. Standard escape sequences applied. |
| Multi-line | """...""" |
Contains newlines. Requires AllowMultiline flag. |
Raw strings disable escape sequence processing. Use raw strings for Regex patterns, Windows paths, or content with heavy quotation marks.
Delimiter Calculation:
The writer identifies the longest consecutive sequence of # characters in the source string. It then wraps the string in n+1 hashes to prevent premature termination.
Style Flags:
- RawPaths: Employs raw strings if the value contains
/or\. - PreferRaw: Employs raw strings if the content requires escaping (e.g., internal quotes).
Configure document appearance through the KdlWriterOptions record.
| Option | Values | Description |
|---|---|---|
| IndentType | Spaces, Tabs |
Sets the character used for nesting. |
| IndentSize | Two, Four |
Sets the count of spaces per level (ignored for Tabs). |
| EscapeUnicode | true, false |
If true, non-ASCII characters emit as \u{XXXX}. |
| NewLine | \n |
Internal constant. All output uses LF line endings. |
Apply custom formatting by passing options to the serializer.
var options = new KdlSerializerOptions
{
StringStyle = KdlStringStyle.RawPaths | KdlStringStyle.AllowBare,
Writer = new KdlWriterOptions
{
IndentType = KdlWriterIndentType.Spaces,
IndentSize = KdlWriterIndentSize.Two,
EscapeUnicode = true
}
};
string kdl = KdlSerializer.Serialize(myObject, options);The Kuddle.Net.Extensions.Configuration package enables KDL as a configuration source.
Installation:
dotnet add package Kuddle.Net.Extensions.ConfigurationImplementation:
Add the KDL provider to the ConfigurationBuilder.
using Kuddle.Extensions.Configuration;
var config = new ConfigurationBuilder()
.AddKdlFile("appsettings.kdl", optional: false, reloadOnChange: true)
.Build();
string connection = config["database:connection-string"];KDL document structures map to flattened .NET configuration keys using the following logic:
| KDL Structure | Configuration Key | Example |
|---|---|---|
| Nested Nodes | Colon Separator | server { port 80 } → server:port |
Anonymous Nodes (-) |
Numeric Index | - "val" → :0, :1 |
| Node Arguments | Numeric Index | endpoints "a" "b" → endpoints:0, endpoints:1 |
| Properties | Key Name | node key="val" → node:key |
Kuddle.Net uses specific exceptions for syntax and mapping failures.
| Exception | Root Cause | Critical Properties |
|---|---|---|
| KuddleParseException | Syntax error in KDL source. | Line, Column, Offset |
| KuddleSerializationException | CLR/KDL type mismatch. | Message |
| KuddleValidationException | Reserved type format failure. | Errors (Collection) |
| KdlConfigurationException | Invalid attribute configuration. | Message |
KuddleParseException provides exact locations for syntax correction:
- Line: 1-based line number.
- Column: 1-based column number.
- Offset: 0-based character position from document start.