Lucas Coelho
Lucas Coelho

Reputation: 603

Merging YAML in C#

I want to merge two YAML files into one, in such a way that it would avoid duplicates and merge the attributes. As an example, having two yaml files as presented below:

yaml1:

first_name: "John"
last_name: "Smith"
enabled: false
roles:
  - user

yaml2:

enabled: true
roles:
  - user
  - admin

I would expect the following result:

first_name: "John"
last_name: "Smith"
enabled: true
roles:
  - user
  - admin

So far I was able to do it converting YAML to JSON and using this example, however, I wanted to know a way using the C# YAML libraries (like yamldotnet and SharpYaml).

Upvotes: 5

Views: 1970

Answers (2)

js.mouret
js.mouret

Reputation: 443

Using YamlDotNet, if the document has a unique type not used by its parts, this trick can be helpful:

using YamlDotNet.Serialization;
using YamlDotNet.Serialization.ObjectFactories;

public Document Load(string[] contents) {
    var document = new Document();
            
    var factory = new DefaultObjectFactory();
    var deserializer = new DeserializerBuilder()
        .WithObjectFactory(type => type == typeof(Document) ? document : factory.Create(type))
        .Build();

    foreach (var content in contents) {
        deserializer.Deserialize<Document>(content);
    }

    return document;
}

Upvotes: 2

ganchito55
ganchito55

Reputation: 3607

I have achieved it using Yamldotnet and then using the following algorithm:

  1. Use the first yaml as base

  2. Try to override the first yaml with the second one

    2.1 If it's a new field add it

    2.2 If the field exists and it isn't a collection, override it

    2.3 If the field exists and it is a collection, merge the collection

    2.3.1 If the new value is not a collection, add it to the collection

    2.3.2 If the new value is a collection add each nonduplicated element to the collection. For this reason I use a HashSet, a collection that doesn't allow duplicated items.

The code:

static void Main(string[] args)
        {

            var deserializer = new DeserializerBuilder()
               .WithNamingConvention(new CamelCaseNamingConvention())
               .Build();

            var object1 = deserializer.Deserialize<Dictionary<string,object>>(@"---
first_name: ""John""
last_name: ""Smith""
enabled: false
roles:
    - user
...");

            var object2 = deserializer.Deserialize<Dictionary<string, object>>(@"---
enabled: true
roles:
  - user
  - admin
...");
            foreach (var tuple in object2)
            {
                if (!object1.ContainsKey(tuple.Key))
                {
                    object1.Add(tuple.Key, tuple.Value);
                    continue;
                }

                var oldValue = object1[tuple.Key];
                if (!(oldValue is ICollection))
                {
                    object1[tuple.Key] = tuple.Value;
                    continue;
                }


                //Merge collection
                var mergeCollection = new HashSet<object>(oldValue as IEnumerable<object>);
                if (!(tuple.Value is ICollection))
                    mergeCollection.Add(tuple.Value);
                else
                {
                    foreach (var item in tuple.Value as IEnumerable)
                    {
                        mergeCollection.Add(item);
                    }
                }

                object1[tuple.Key] = mergeCollection;                                                             

            }

            var result = new SerializerBuilder().Build().Serialize(object1);

        }

I hope this can help you :)

Upvotes: 5

Related Questions