SharpYaml provides a JsonSerializer-style serialization API.
using SharpYaml;
var yaml = YamlSerializer.Serialize(new { Name = "Ada" });
var model = YamlSerializer.Deserialize<Person>(yaml);
SharpYaml also provides Stream overloads (UTF-8) to avoid StreamReader/StreamWriter boilerplate:
using System.IO;
using SharpYaml;
using var stream = File.OpenRead("config.yml");
var config = YamlSerializer.Deserialize<MyConfig>(stream);
If you want to avoid string allocations when emitting YAML, you can write directly to an IBufferWriter<char>:
using System.Buffers;
using SharpYaml;
var buffer = new ArrayBufferWriter<char>();
YamlSerializer.Serialize(buffer, new { Name = "Ada" });
var yaml = new string(buffer.WrittenSpan);
YamlSerializerOptions is immutable and can be cached and reused.
Common options:
PropertyNamingPolicy and DictionaryKeyPolicy (JsonNamingPolicy)WriteIndented and IndentSizeBlockSequenceMappingStyle and BlockSequenceSequenceStyleDefaultIgnoreConditionPropertyNameCaseInsensitivePreferredObjectCreationHandlingReferenceHandling (anchors/aliases)using System.Text.Json;
using SharpYaml;
var options = new YamlSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
DefaultIgnoreCondition = YamlIgnoreCondition.WhenWritingNull,
};
By default, YamlSerializerOptions.PropertyNamingPolicy is null, meaning CLR member names are used as-is for YAML mapping keys.
This matches the default behavior of JsonSerializer (outside of ASP.NET defaults).
If you want camelCase keys, set PropertyNamingPolicy = JsonNamingPolicy.CamelCase.
ReferenceHandling preserves CLR object identity, not the original YAML spelling or structure.
In particular, YAML merge keys (<<: *defaults) are expanded during deserialization. A mapping
that merges defaults and adds or overrides entries is a distinct object, not an alias of the
defaults mapping. Serializing it writes its resulting entries, even with Preserve or
PreserveMinimal. Original anchor names, comments, scalar spellings (such as hexadecimal
numbers), and formatting are not retained by object mapping.
For example, in a Docker Compose file, environment: { <<: *common-environment, EXTRA: value }
becomes an environment dictionary containing both the defaults and EXTRA. Reference handling
cannot infer which entries originally came from a merge.
For an unchanged, lossless round trip, use the syntax layer:
using SharpYaml.Syntax;
var tree = YamlSyntaxTree.Parse(File.ReadAllText("compose.yaml"));
File.WriteAllText("compose-copy.yaml", tree.ToFullString());
To modify selected source ranges while keeping merges and surrounding comments intact, use
YamlSyntaxTree.WithTextChange.
For event-level transformations that retain anchors, aliases, and merge keys but may reformat the output and discard comments, use the parser and emitter directly. The mutable model layer is not a lossless alternative: it currently materializes aliases as copies.
| Option | Default | Meaning |
|---|---|---|
PropertyNamingPolicy |
null |
Optional renaming for CLR member names. |
DictionaryKeyPolicy |
null |
Optional renaming for dictionary keys during serialization. |
PropertyNameCaseInsensitive |
false |
Case-insensitive property matching when reading. |
PreferredObjectCreationHandling |
JsonObjectCreationHandling.Replace |
Controls whether members are replaced or populated during deserialization. |
DefaultIgnoreCondition |
YamlIgnoreCondition.Never |
Skips null/default values when writing. |
WriteIndented |
true |
Enables indentation. |
IndentSize |
2 |
Spaces per indent level when WriteIndented is enabled. |
MappingOrder |
YamlMappingOrderPolicy.Declaration |
Preserves declaration order by default (diff-friendly in code review). |
BlockSequenceMappingStyle |
YamlSequenceItemStyle.Compact |
Writes mappings in block sequences as - key: value by default. |
BlockSequenceSequenceStyle |
YamlSequenceItemStyle.Expanded |
Controls whether nested sequences in block sequences start on the dash line or the following line. |
Schema |
YamlSchemaKind.Core |
Controls scalar resolution rules (YAML 1.2). |
DuplicateKeyHandling |
YamlDuplicateKeyHandling.Error |
Controls behavior when duplicate keys are encountered. |
ReferenceHandling |
YamlReferenceHandling.None |
Enables anchor/alias preservation. Preserve anchors every reference object; PreserveMinimal performs a pre-serialization pass and anchors only shared or cyclic objects. |
ScalarStylePreferences |
new | Controls scalar emission styles. |
PolymorphismOptions |
new | Controls polymorphism behaviors. |
UnsafeAllowDeserializeFromTagTypeName |
false |
Allows tag-based activation by runtime type name (use only with trusted input). |
TypeInfoResolver |
null |
Provides metadata (generated or custom) for reflection-free serialization. |
SourceName |
null |
Used for error messages (file/path) when throwing YamlException. |
Mappings written as block sequence items use compact style by default:
contexts:
- name: default
target: localhost
Set BlockSequenceMappingStyle = YamlSequenceItemStyle.Expanded to emit the first key on the following line instead. Nested sequences can be controlled separately with BlockSequenceSequenceStyle, and member-level overrides are available through YamlBlockSequenceItemStyleAttribute.
SharpYaml follows the same default as System.Text.Json: member values are replaced unless you opt into population.
using System.Text.Json.Serialization;
using SharpYaml;
var options = new YamlSerializerOptions
{
PreferredObjectCreationHandling = JsonObjectCreationHandling.Populate,
};
You can also opt in per type or per member with JsonObjectCreationHandlingAttribute.
Replace is the default.Populate reuses existing mutable reference-type members such as child objects, List<T>, IList<T>, ICollection<T>, Dictionary<TKey, TValue>, and IDictionary<TKey, TValue>.[JsonObjectCreationHandling(...)] overrides the type or options default.Populate. A readonly struct property marked with Populate throws at runtime, matching System.Text.Json.SharpYaml can resolve serialization metadata in two ways:
YamlSerializerContext and YamlTypeInfo<T> (recommended for NativeAOT).If you disable reflection (see below), POCO/object mapping requires metadata via YamlTypeInfo<T> or YamlSerializerOptions.TypeInfoResolver.
Built-in primitives and untyped containers remain supported without reflection.
AppContext.SetSwitch("SharpYaml.YamlSerializer.IsReflectionEnabledByDefault", false);
You can use a generated context in three styles:
YamlTypeInfo<T> property (recommended).var yaml = YamlSerializer.Serialize(value, MyYamlContext.Default.MyConfig);
Type).var yaml = YamlSerializer.Serialize(value, typeof(MyConfig), MyYamlContext.Default);
Prefer the overloads that accept a YamlSerializerContext or a YamlTypeInfo<T> directly to avoid reflection and reduce configuration overhead.