Mapping attributes
Bodu.Text.Yaml shapes how a type maps to the wire with the same shared attribute family as its TOML and Bencode siblings: every attribute lives in the Bodu.Text.Serialization namespace and derives SerializationAttribute. The patterns below show the most common members; the wider family - [Converter], [PropertyOrder], [Required], [Include], [ExtensionData], [Constructor], [ObjectCreationHandling], [NamingPolicy], and [StringEnumMemberName] - works exactly as it does in the siblings.
Pattern 1 - Rename a member
PropertyNameAttribute pins the serialized key for one member, beating any naming policy:
using Bodu.Text.Serialization;
public sealed class Profile
{
[PropertyName("display-name")]
public string DisplayName { get; set; } = "Ada";
}
display-name: Ada
The explicit name always wins over the options-level naming policy.
Pattern 2 - Apply a naming policy
The options-level PropertyNamingPolicy renames every member that does not carry an explicit [PropertyName]. The shared NamingPolicy set applies - CamelCase, SnakeCaseLower/SnakeCaseUpper, and KebabCaseLower/KebabCaseUpper - a type can pin its own policy with [NamingPolicy(KnownNamingPolicy…)], and the YamlSerializerDefaults scenario presets configure it for you (Web selects camel-case naming with case-insensitive matching):
var options = new YamlSerializerOptions
{
PropertyNamingPolicy = NamingPolicy.SnakeCaseLower,
};
public sealed class RetryPolicy
{
public int MaxRetryCount { get; set; } = 5;
}
string yaml = YamlSerializer.Serialize(new RetryPolicy(), options);
max_retry_count: 5
On the read path, PropertyNameCaseInsensitive lets mapping keys match members regardless of case.
Note
If two members resolve to the same wire key under the active policy and attributes - for example a [PropertyName("name")] that collides with another member's policy-derived name - serialization throws InvalidOperationException rather than silently emitting a duplicate key. Choose keys that stay unique after the policy is applied.
Pattern 3 - Exclude a member
IgnoreAttribute drops a member unconditionally, or under a per-member condition:
using Bodu.Text.Serialization;
public sealed class Account
{
public string Name { get; set; } = "svc";
[Ignore]
public string? Secret { get; set; }
[Ignore(Condition = IgnoreCondition.WhenWritingNull)]
public string? Comment { get; set; }
}
Name: svc
To omit members whose value happens to be null (or the type default) across the whole document, set the serializer-wide DefaultIgnoreCondition instead (Pattern 4); a member-level [Ignore(Condition = …)] overrides it.
Pattern 4 - Options flags
The remaining shaping is on YamlSerializerOptions:
| Flag | Effect |
|---|---|
DefaultIgnoreCondition |
IgnoreCondition - Never (default) writes every member; WhenWritingNull omits null members; WhenWritingDefault also omits type-default values. |
WriteEnumsAsStrings |
true (default) writes enums as member-name strings; false writes the underlying integer. |
PropertyNameCaseInsensitive |
Matches mapping keys to members case-insensitively on read. |
IncludeFields |
Includes public fields alongside properties. |
UnmappedMemberHandling |
UnmappedMemberHandling - Skip (default) ignores keys that map to no member; Disallow raises YamlSerializationException. |
var options = new YamlSerializerOptions
{
DefaultIgnoreCondition = IgnoreCondition.WhenWritingNull,
WriteEnumsAsStrings = false, // enums as integers
IncludeFields = true, // public fields participate
PropertyNameCaseInsensitive = true, // case-insensitive key matching
UnmappedMemberHandling = UnmappedMemberHandling.Disallow,
};
With IncludeFields set, a public field maps exactly like a property - it honours the naming policy and [PropertyName] / [Ignore]:
public sealed class Counter
{
public int Total { get; set; }
public int Retries; // included when IncludeFields is true
}
A property is written whenever it has a public getter, but it is only read back when it also has a public setter - a get-only property serialises out and is silently skipped on deserialisation. Members are emitted properties-first (in reflection order) and then fields, so a field never interleaves with the properties even when IncludeFields is set.
The wider attribute family
The remaining shared attributes work in YAML exactly as in the siblings:
| Attribute | Effect |
|---|---|
[Converter(typeof(…))] |
Binds a converter or converter factory to a member, property, or type - for example [Converter(typeof(YamlStringEnumConverter<Status>))]. |
[PropertyOrder(n)] |
Orders members on write (lower first; unattributed members keep reflection order at order 0). |
[Required] |
A missing key on read raises YamlSerializationException (the C# required keyword is honored too). |
[Include] |
Surfaces a member with a non-public setter, or a field without IncludeFields. |
[ExtensionData] |
Captures unmapped keys into a Dictionary<string, object?> member and writes them back out. |
[Constructor] |
Selects the deserialization constructor for parameterized/immutable types. |
[ObjectCreationHandling(…)] |
Per-member replace-vs-populate on read, overriding PreferredObjectCreationHandling. |
[NamingPolicy(…)] |
Pins a naming policy for one type. |
[StringEnumMemberName("…")] |
Renames one enum member on the wire (honored by the default enum handling and the enum converters). |
For a custom wire form the attributes cannot express - a value type rendered as a single scalar, or a type the defaults reject - write a custom converter and register it on options.Converters.
Precedence at a glance
When several settings could govern the same member, the closest one wins:
- a member-level
[PropertyName](name),[Ignore](exclusion), or[Converter](wire form); - a type-level
[NamingPolicy]or[Converter]; - the serializer options (
PropertyNamingPolicy,DefaultIgnoreCondition,IncludeFields,UnmappedMemberHandling, …).
Where to go next
- Writing converters - the route for everything beyond renaming and ignoring.
- Built-in converter catalog - which types map without any attribute at all.
- Using YAML - the end-to-end walk-through.
- Bodu.Text.Yaml core concepts - where attributes fit in the converter-resolution picture.
- Bodu serializer guides and the Text & Serialization guides.
- API reference - PropertyNameAttribute, IgnoreAttribute, NamingPolicy.