Nett
Nett copied to clipboard
How to convert a dictionary whose value is an interface type?
Hello, I have a interface and some derived classes, like:
public interface ISomeInterface
{
int SomeInteger { get; set; }
string SomeString { get; set; }
}
public class ClassA : ISomeInterface
{
public int SomeInteger { get; set; } = 1;
public string SomeString { get; set; } = "This is Class A.";
public string ClassAProperty { get; set; } = "Only for Class A!";
}
public class ClassB : ISomeInterface
{
public int SomeInteger { get; set; } = 2;
public string SomeString { get; set; } = "This is Class B.";
public string ClassBProperty { get; set; } = "Only for Class B!";
}
And I have a entity class with a IDictionary property with the interface above as value type:
public class TomlEntity
{
public IDictionary<string, ISomeInterface> Dictionary { get; set; } =
new Dictionary<string, ISomeInterface>();
}
Now I can write the entity to string but cannot read into it:
var entity = new TomlEntity();
var classA = new ClassA();
var classB = new ClassB();
entity.Dictionary.Add(nameof(ClassA), classA);
entity.Dictionary.Add(nameof(ClassB), classB);
var result = Toml.WriteString(entity);
Console.Write(result);
var reader = Toml.ReadString<TomlEntity>(result); // error
In fact I will have more derived classes and I have their own reflection types, how can I specify their type and instantiate them into the dictionary?
Unfortunately this use case is not natively supported at the moment as a quick short investigations brings up.
At the moment you can only do the following workaround:
var result = Toml.WriteString(entity);
var settings = TomlSettings.Create(cfg => cfg
.ConfigureType<IDictionary<string, ISomeInterface>>(tc => tc
.CreateInstance(() => new Dictionary<string, ISomeInterface>()))
.ConfigurePropertyMapping(pm => pm
.OnTargetPropertyNotFound(FailedToMapToTarget)));
void FailedToMapToTarget(string[] keyChain, object target, TomlObject source)
{
var tgtDict = (Dictionary<string, ISomeInterface>)target;
TomlTable srcTable = (TomlTable)source;
var key = keyChain.Last();
if (key == "ClassA")
{
tgtDict.Add(key, srcTable.Get<ClassA>());
}
else if (key == "ClassB")
{
tgtDict.Add(key, srcTable.Get<ClassB>());
}
}
Console.Write(result);
var read = Toml.ReadString<TomlEntity>(result, settings);
But I think I will add this functionality in the near future. This will be a breaking change and the configuration will have to be changed to something like this, that is conceptually better
var settings = TomlSettings.Create(cfg => cfg
.ConfigureType<IDictionary<string, ISomeInterface>>(tc => tc
.CreateInstance(() => new Dictionary<string, ISomeInterface>()))
.ConfigureType<ISomeInterface>(tc => tc
.WithConversionFor<TomlTable>(conv => conv
.FromToml(SomeInterfaceFromTomlTable)))
var read = Toml.ReadString<TomlEntity>(result, settings);
ISomeInterface SomeInterfaceFromTomlTable(string key, TomlTable tbl)
{
if (key == "ClassA") { return tbl.Get<ClassA>(); }
else if (key == "ClassB") { return tbl.Get<ClassB>(); }
else { return null; } // probably throw EXC
}
@paiden Thanks, it works.