2

I want to load polymorphic data from appsettings. This is a test setup.

async Task Main()
{
    var cfg = new
    {
        PolySection = new PolyCfg
        {
            Property = new List<IPolyProperty> 
            {
                new PolyImplementationOne { Data = 100 },
                new PolyImplementationTwo { Data = 200.5 },
            },
            JustAnInt = 1000
        },
    };
    
    var jsonConfig = JsonSerializer.Serialize(cfg);
    
    // Test
    var jsonSection = JsonSerializer.Serialize(cfg.PolySection).Dump();
    JsonSerializer.Deserialize<PolyCfg>(jsonSection).Dump();
    
    var cfgstrm = new MemoryStream(Encoding.Default.GetBytes(jsonConfig));

    var host = Host.CreateDefaultBuilder()
            .ConfigureAppConfiguration(config => config.AddJsonStream(cfgstrm))         
            .ConfigureServices((builder, services) =>
            {
                services.Configure<PolyCfg>(builder.Configuration.GetSection("PolySection"));
                services.AddHostedService<Service>();
            })
            .Build()
            .RunAsync();
}

public class PolyCfg
{ 
    public IList<IPolyProperty> Property { get; set; } 
    public int JustAnInt { get; set; }
}

[JsonDerivedType(typeof(PolyImplementationOne), nameof(PolyImplementationOne))]
[JsonDerivedType(typeof(PolyImplementationTwo), nameof(PolyImplementationTwo))]
public interface IPolyProperty
{ 
    
}

public class PolyImplementationOne: IPolyProperty
{ 
    public int Data { get; set; }
}

public class PolyImplementationTwo: IPolyProperty
{
    public double Data { get; set; }
}

public class Service : BackgroundService
{
    public Service(IOptions<PolyCfg> opts, ILogger<Service> logger)
    {
        logger.LogInformation("The int is: {justanint}", opts.Value.JustAnInt); // OK: return 1000
        logger.LogInformation("Number of items: {items}", opts.Value.Property.Count); // NOK: return 0
    }
    
    protected override Task ExecuteAsync(CancellationToken stoppingToken)
    {
        return Task.CompletedTask;
    }
}

The config is serialized correctly:

{"PolySection":{"Property":[{"$type":"PolyImplementationOne","Data":100},{"$type":"PolyImplementationTwo","Data":200.5}],"JustAnInt":1000}}

When I try to deserialize the polysection string itself only, that is also ok, hence the polymorphism is working per se:

enter image description here

However, the IOptions injected has only the integer in it, the list is not deserialized. How System.text.Json handles polymorphism is a pain on its own - but I can live with that if it is usable at least verywhere. Json.Net handles this polymorphism much better, but I can't figure out how to use that in this scenario.

  1. Can I make somehow this to work with system.text.json?
  2. Is there a way to replace serializer in configuration management with Json.Net?

[Update 1]

I have added created custom converters and attached to properties anc types. It seems, that they are also not used when reading the appsetting file.

  1. Is there any way to inject converters in that deserialisation process?

[Update 2]

dbc
  • 104,963
  • 20
  • 228
  • 340
ZorgoZ
  • 2,974
  • 1
  • 12
  • 34
  • Right, configuration is just a fancy `Dictionary`. Loading appsettings.json, flattens the json hierarchy to `.Add("parent:child:property", "value")`. – Jeremy Lakeman May 26 '23 at 04:58

0 Answers0