Chang Liu
Chang Liu

Reputation: 61

YamlDotNet: How to customize deserializer to handle same key with different type of value

config.yaml:

App1:
  settings:
    redirectUrl: http://www.test.com
App2:
  settings:
    redirectUrl:
      test: http://www.test.com
      prod: http://www.prod.com

C# objects

public class Config
 {
    public Dictionary<string, App> Apps { get; set; }
 }

public class App
 {
    public Dictionary<string, Setting> Settings { get; set; }
 }

public class Setting
 {
    public string Test {get;set;}
    public string Prod {get;set;}
 }

Expected result:

for App1, both test and prod are the same http://www.test.com

for App2, test is http://www.test.com and prod is http://www.prod.com

I have done a lot of research but cannot figure out how to achieve this by using custom deserializer. Please help..

Upvotes: 2

Views: 2634

Answers (1)

Antoine Aubry
Antoine Aubry

Reputation: 12469

You could make your Settings class implement IYamlConvertible. That interface allows you to control the serialization process. A possible implementation could be:

public class SettingsBase
{
    public string Test { get; set; }
    public string Prod { get; set; }
}

public class Setting : SettingsBase, IYamlConvertible
{
    public void Read(IParser parser, Type type, ObjectDeserializer nestedObjectDeserializer)
    {
        var scalar = parser.Allow<Scalar>();
        if (scalar != null)
        {
            Test = Prod = scalar.Value;
        }
        else
        {
            var values = (SettingsBase)nestedObjectDeserializer(typeof(SettingsBase));
            this.Test = values.Test;
            this.Prod = values.Prod;
        }
    }
    
    public void Write(IEmitter emitter, ObjectSerializer nestedObjectSerializer)
    {
        if (Test == Prod)
        {
            nestedObjectSerializer(Test);
        }
        else
        {
            nestedObjectSerializer(new SettingsBase { Test = this.Test, Prod = this.Prod });
        }
    }
}

Here I've used a base class (SettingsBase) as a way to easily (de)serialize the properties, but one could use another strategy. Basically the Read method checks if the current token is a scalar. If so, it uses its value. Otherwise, it deserializes as a SettingsBase. The Write method does the opposite.

A fully working example is available here

Upvotes: 2

Related Questions