Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to serialize the !include Directive in YamlDotNet #880

Open
martindybal opened this issue Dec 27, 2023 · 8 comments
Open

How to serialize the !include Directive in YamlDotNet #880

martindybal opened this issue Dec 27, 2023 · 8 comments

Comments

@martindybal
Copy link

Description:

I have a YAML configuration that includes external files using the !include directive, like this:

views:
  - !include views/home.yaml
  - title: Světnice
    path: dashboard
    badges: ...

I'm using YamlDotNet for YAML serialization in my C# project, and I'm wondering how to properly serialize this YAML structure while ensuring that the !include directive is correctly processed to include external files.

I'm tried

            views = new[]
            {
                new YamlScalarNode("!include views/home.yaml"),
                new
                {
                    title = dashboard.Title,
                    path = dashboard.UrlSlug,
                    badges = dashboard.GetBadges().Select(badge => badge.ToYml()),
                    cards = dashboard.GetCards().Select(card => card.ToYml())
                }.ToYml(),
            }

also

            views = new object[]
            {
                "!include views/home.yaml",
                new
                {
                    title = dashboard.Title,
                    path = dashboard.UrlSlug,
                    badges = dashboard.GetBadges().Select(badge => badge.ToYml()),
                    cards = dashboard.GetCards().Select(card => card.ToYml())
                },
            }

The result is both cases the same:

views:
- '!include views/home.yaml'  #this should not be a string
- title: Světla
  path: lovelace-lights
  badges:  ...

Is there something like RawYamlNode ?

@EdwardCooke
Copy link
Collaborator

On the YamlScalarNode try setting the scalar type to plain. That should do it.

@martindybal
Copy link
Author

@EdwardCooke thanks for the answer. The NodeType property is get only.

public override YamlNodeType NodeType

@EdwardCooke
Copy link
Collaborator

The property is actually style. Sorry about that.

public ScalarStyle Style { get; set; }

@martindybal
Copy link
Author

@EdwardCooke the result is same.

new YamlScalarNode("!include views/home.yaml")
{
    Style = ScalarStyle.Plain
}

- '!include views/home.yaml'

@martindybal
Copy link
Author

I hope this is only temporary solution :)

var yamlNode = YamlHelper.Include("views/home.yaml");
var serializer = new SerializerBuilder().Build();
var yaml = serializer.Serialize(yamlNode);
System.Console.WriteLine(yaml);

public static class YamlHelper
{
    public static YamlNode Include(string fileName)
    {
        return ToYaml($"!include {fileName}");
    }
        
    public static YamlNode ToYaml(string yaml)
    {
        var yamlStream = new YamlStream();
        yamlStream.Load(new StringReader(yaml));
        return yamlStream.Documents[0].RootNode;
    }
}

!include views/home.yaml

@EdwardCooke
Copy link
Collaborator

What if you set the tag property to !include or include? Then set the value to your file.

@EdwardCooke
Copy link
Collaborator

Just got to spend more time on this. This is much cleaner, using a yaml type convert and an include object.

using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;

var serializer = new SerializerBuilder()
    .WithTagMapping("!include", typeof(IncludedObject))
    .WithTypeConverter(new TestTypeConverter())
    .WithNamingConvention(CamelCaseNamingConvention.Instance)
    .Build();


var o = new object[] {
    new IncludedObject{ Filename = "test.yaml" },
    new Book
    {
        Title = "a title",
        Path = "a path",
        Badges = "some badges"
    }
};
var serialized = serializer.Serialize(o);
Console.WriteLine(serialized);

class Outer
{
    public object[] Items { get; set; }
}
class Book
{
    public string Title { get; set; }
    public string Path { get; set; }
    public string Badges { get; set; }
}

// This is the magic that makes it easily re-usable
class TestTypeConverter : IYamlTypeConverter
{
    public bool Accepts(Type type) => type == typeof(IncludedObject);

    public object? ReadYaml(IParser parser, Type type)
    {
        throw new NotImplementedException();
    }

    public void WriteYaml(IEmitter emitter, object? value, Type type)
    {
        if (type != typeof(IncludedObject))
        {
            return;
        }
        if (value is IncludedObject o)
        {
            emitter.Emit(new Scalar(null, "!include", o.Filename, ScalarStyle.Plain, false, false));
        }
    }
}

class IncludedObject
{
    public string Filename { get; set; } = string.Empty;
}

Results in

- !include test.yaml
- title: a title
  path: a path
  badges: some badges

@EdwardCooke
Copy link
Collaborator

If you want to see how to read in an include directive, checkout my answer here.

#909

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants