VYaml Documentation

repository·master·Indexed 19 days ago

https://github.com/hadashia/vyaml

A high-performance, low-allocation YAML 1.2 implementation written in pure C# for .NET and Unity. VYaml handles UTF-8 byte sequences directly to minimize memory footprint and provides features including a YAML parser and emitter, serialization/deserialization for user-defined types via source generators, support for YAML anchors and aliases, and specialized Unity type support.

Tokens
4.8K
Snippets
13
Records
16
Agent score
17%

What's inside VYaml

  1. Overview of VYaml features

    master

    VYaml is a high-performance, low-allocation YAML 1.2 implementation for .NET and Unity. It is designed to handle UTF-8 byte sequences directly to minimize memory footprint.

    Key Capabilities:

    • YAML Parser (Reader): Supports YAML 1.2 and Unity's non-standard serialized YAML format (which may include a "stripped" symbol in the document start line).
    • YAML Emitter (Writer): Supports primitive types, various scalar styles (plain, double-quoted, literal), and multiple sequence/mapping styles (block, flow).
    • Serialization/Deserialization:
      • Converts between YAML and C# user-defined types.
      • Supports dynamic for primitive collections.
      • Supports interface-typed and abstract class-typed objects.
      • Supports YAML anchors (&) and aliases (*).
      • Supports deserializing multiple YAML documents into a C# collection.
    • Customization: Allows renaming keys and ignoring members during serialization.
  2. Implicit primitive type conversion in VYaml

    master

    VYaml follows the YAML Core Schema for implicit type interpretation of scalars. When parsing YAML, the following patterns are automatically resolved to their respective types:

    PatternResolved Type
    null, Null, NULL, ~, or empty /* Empty */null
    true, True, TRUE, false, False, FALSEboolean
    [-+]? [0-9]+ (Base 10)int
    0o [0-7]+ (Base 8)int
    0x [0-9a-fA-F]+ (Base 16)int
    [-+]? ( \. [0-9]+ | [0-9]+ ( \. [0-9]* )? ) ( [eE] [-+]? [0-9]+ )?float
    [-+]? ( \.inf | \.Inf | \.INF )float (Infinity)
    \.nan | \.NaN | \.NANfloat (Not a number)
  3. Customize serialization behavior with IYamlFormatter

    master

    You can customize how specific C# types are serialized or deserialized by implementing IYamlFormatter<T>. To integrate these custom formatters into the YamlSerializer, use YamlSerializerOptions with a CompositeResolver.

    It is recommended to include StandardResolver.Instance at the end of your resolver list to ensure default behavior remains available for types you haven't explicitly customized.

    var options = new YamlSerializerOptions
    {
        Resolver = CompositeResolver.Create(
            new IYamlFormatter[]
            {
                new YourCustomFormatter1(), // Your custom formatter
            },
            new IYamlFormatterResolver[]
            {
                new YourCustomResolver(),  // Your custom resolver
                StandardResolver.Instance, // Fallback to default behavior
            })
    };
    
    YamlSerializer.Deserialize<T>(yaml, options);
    YamlSerializer.Serialize(obj, options);
  4. How Enums are serialized

    master

    By default, enum values are serialized in camelCase with a leading lowercase letter.

    Customizing Enum Serialization

    • Aliases: Use [EnumMember(Value = "alias")] or [DataMember(Value = "alias")] to specify a custom string representation.
    • Naming Convention: You can apply a NamingConvention to an enum using [YamlObject(NamingConvention.SnakeCase)].
    • Flags Enums: Enums marked with [Flags] are serialized by joining member names with | (e.g., read | write). Deserialization accepts both | and , as separators.
    enum Foo
    {
        [EnumMember(Value = "item1-alias")]
        Item1,
    }
    // Serializes to: "item1-alias"
    
    [Flags]
    enum Permissions
    {
        Read = 1,
        Write = 2,
    }
    // Serializes Read | Write to: "read | write"
  5. Implement Polymorphism (Union) with YAML tags

    master

    VYaml supports deserializing interface-typed or abstract-class-typed objects using a feature called Union. This allows you to use YAML tags (e.g., !foo) to identify which concrete type to instantiate.

    1. Annotate the interface or abstract class with [YamlObjectUnion("tag", typeof(ConcreteType))] for each possible implementation.
    2. Annotate the interface/abstract class itself with [YamlObject].
    3. Ensure all concrete types are also annotated with [YamlObject].
    4. Each union tag must be unique.
    [YamlObject]
    [YamlObjectUnion("!foo", typeof(FooClass))]
    [YamlObjectUnion("!bar", typeof(BarClass))]
    public partial interface IUnionSample
    {
    }
    
    [YamlObject]
    public partial class FooClass : IUnionSample
    {
        public int A { get; set; }
    }
    
    [YamlObject]
    public partial class BarClass : IUnionSample
    {
        public string? B { get; set; }
    }
    
    // Deserializing a tagged document
    var obj = YamlSerializer.Deserialize<IUnionSample>(UTF8.GetBytes("!foo { a: 100 }"));
    // obj is now an instance of FooClass
  6. Serialize and Deserialize objects with VYaml

    master

    To use VYaml for serialization, define a struct or class and annotate it with the [YamlObject] attribute. The class must be marked as partial because VYaml uses Source Generators to create the necessary metaprogramming code.

    Serialization

    • YamlSerializer.Serialize<T>(obj): Returns a UTF-8 byte array (recommended for files/data stores).
    • YamlSerializer.SerializeToString(obj): Returns a C# string (involcur UTF-16 conversion overhead).

    Deserialization

    • await YamlSerializer.DeserializeAsync<T>(stream): Deserializes from a stream asynchronously.
    • YamlSerializer.Deserialize<T>(utf8Bytes): Deserializes from a UTF-8 byte array.
    • YamlSerializer.Deserialize<dynamic>(utf8Bytes): Deserializes into a dynamic object for schema-less access.

    Ignoring Members

    Use the [YamlIgnore] attribute to prevent a public member from being serialized.

    using VYaml.Annotations;
    
    [YamlObject]
    public partial class Sample
    {
        public string A;
        public string B { get; set; }
    
        [YamlIgnore]
        public int PublicProperty2 => PublicProperty + PublicField;
    }
    
    // Usage
    var utf8Yaml = YamlSerializer.Serialize(new Sample { A = "hello", B = "foo" });
    var sample = await YamlSerializer.DeserializeAsync<Sample>(stream);
  7. Install VYaml in Unity

    master

    VYaml requires Unity 2021.3 or later. Since version 1.0, it is distributed via NuGetForUnity.

    1. Install NuGetForUnity.
    2. Open the NuGet window via NuGet > Manage NuGet Packages.
    3. Search for "VYaml" and install it.

    Installing Unity-specific extensions

    To add Unity-specific extensions, use the Unity Package Manager:

    1. Open Window > Package Manager.
    2. Click the [+] button and select Add package from git URL.
    3. Enter the following URL: https://github.com/hadashiA/VYaml.git?path=VYaml.Unity/Assets/VYaml#1.4.0
    https://github.com/hadashiA/VYaml.git?path=VYaml.Unity/Assets/VYaml#1.4.0
  8. Configure Unity type support

    master

    To enable support for Unity types (like Vector3, Color, Quaternion, etc.), follow these steps:

    1. Install the VYaml Unity package.
    2. Configure the YamlSerializer to use a CompositeResolver that includes the UnityResolver.

    If the Unity.Mathematics package is also installed, support is automatically enabled for types like float2, int3x3, quaternion, etc.

    YamlSerializer.DefaultOptions = new YamlSerializerOptions
    {
        Resolver = CompositeResolver.Create(new IYamlFormatterResolver[]
        {
            StandardResolver.Instance,
            UnityResolver.Instance,
        })
    };
  9. Configure Naming Conventions and Member Aliases

    master

    By default, VYaml maps C# property names to YAML keys using lowerCamelCase. You can customize this globally via YamlSerializerOptions or per-type/per-member using attributes.

    Global Configuration

    Set YamlSerializerOptions.NamingConvention to one of the following:

    • NamingConvention.LowerCamelCase (Default)
    • NamingConvention.UpperCamelCase (e.g., PropertyName)
    • NamingConvention.SnakeCase (e.g., property_name)
    • NamingConvention.KebabCase (e.g., property-name)

    Note: Using a global convention other than the default may cause a slight runtime performance degradation.

    Per-Type and Per-Member Customization

    To avoid performance degradation, use attributes directly on your classes or members:

    • [YamlObject(NamingConvention.SnakeCase)]: Sets the convention for the entire class.
    • [YamlMember("alias-name")]: Sets a specific key name for a single member.
    // Global
    var options = YamlSerializerOptions.Standard;
    options.NamingConvention = NamingConvention.SnakeCase;
    YamlSerializer.Serialize(new A { FooBar = 123 }, options); // { foo_bar: 123 }
    
    // Per-type (No performance penalty)
    [YamlObject(NamingConvention.SnakeCase)]
    public partial class Sample
    {
        public int FooBar { get; init; }
    }
    
    // Per-member
    [YamlObject]
    public partial class Sample
    {
        [YamlMember("foo-bar-alias")]
        public int FooBar { get; init; }
    }
  10. Configure default ignore conditions for null or default values

    master

    You can control whether properties with null or default values are omitted from the serialized output using YamlSerializerOptions.DefaultIgnoreCondition.

    Supported values for YamlIgnoreCondition:

    • YamlIgnoreCondition.Never (Default): Always serialize all properties.
    • YamlIgnoreCondition.WhenWritingNull: Omit properties whose value is null (reference types and Nullable<T>).
    • YamlIgnoreCondition.WhenWritingDefault: Omit properties whose value is null or the default value for value types (e.g., 0, false).
    var options = YamlSerializerOptions.Standard;
    
    // Omit properties that are null
    options.DefaultIgnoreCondition = YamlIgnoreCondition.WhenWritingNull;
    
    // Omit properties that are null or 0/false/etc.
    options.DefaultIgnoreCondition = YamlIgnoreCondition.WhenWritingDefault;
  11. Emit sequences and nested structures with Utf8YamlEmitter

    master

    You can create complex, nested YAML structures by nesting BeginMapping and BeginSequence calls within the Utf8YamlEmitter.

    emitter.BeginSequence();
    {
        // Flow style sequence: [100, "&hoge", bra]
        emitter.BeginSequence(SequenceStyle.Flow);
        {
            emitter.WriteInt32(100);
            emitter.WriteString("&hoge");
            emitter.WriteString("bra");
        }
        emitter.EndSequence();
    
        // Block style mapping with nested sequence
        emitter.BeginMapping();
        {
            emitter.WriteString("key1");
            emitter.WriteString("item1");
    
            emitter.WriteString("key2");
            emitter.BeginSequence();
            {
                emitter.WriteString("nested-item1");
                emitter.WriteString("nested-item2");
                emitter.BeginMapping();
                {
                    emitter.WriteString("nested-key1");
                    emitter.WriteInt32(100);
                }
                emitter.EndMapping();
            }
            emitter.EndSequence();
        }
        emitter.EndMapping();
    }
    emitter.EndSequence();