Asp.Net Core 3.1 Appsettings 不尊重 JsonConverter

edh*_*enn 5 c# application-settings .net-core asp.net-core jsonconverter

在 asp.net core 3.1 中,使用新的 System.Text.Json,我尝试在 appsettings 部分使用自定义 JsonConverter。手动序列化/反序列化尊重转换器就好了,但通过选项模式从 appSettings 读取则不然。这是我所拥有的:

Json转换器。为简单起见,这只是将字符串值转换为大写:

    public class UpperConverter : JsonConverter<string>
    {
        public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) =>
            reader.GetString().ToUpper();

        public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options) =>
            writer.WriteStringValue(value == null ? "" : value.ToUpper());
    }
Run Code Online (Sandbox Code Playgroud)

appsettings 类,在字符串属性上声明转换器:

    public class MyOptions
    {
        public const string Name = "MyOptions";
        [JsonConverter(typeof(UpperConverter))]
        public string MyString { get; set; }
    }
Run Code Online (Sandbox Code Playgroud)

Startup.cs 更改以准备所有内容:

       public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews()
                .AddJsonOptions(options =>
                {
                    options.JsonSerializerOptions.Converters.Add(new UpperConverter());
                });

            services.Configure<MyOptions>(Configuration.GetSection(MyOptions.Name));
        }
Run Code Online (Sandbox Code Playgroud)

当我将 anIOptions<MyOptions>注入 HomeController 时,它读取一个小写值。如果我手动执行JsonSerializer.Deserialize<MyOptions>("{\"MyString\":\"lowercase_manual\"}"),我会得到一个大写字符串。即使我删除了 JsonSerializerOptions 的启动声明。

有谁知道如何让 appsettings/options 模式尊重 JsonConverter?我是否必须在其他地方声明 JsonSerializerOptions?谢谢。

Ian*_*emp 5

了解选项模式是作为两个独立的步骤实现的,这一点很重要:从配置源读取数据,然后将该数据绑定到强类型对象。

读取步骤由各种配置提供程序实现,其中只有一个是 JSON。您可能希望 JSON 提供程序会尊重您的JsonConverter.

那么,绑定步骤似乎是会在意的地方JsonConverter。但是这一步有意完全不知道任何特定的配置提供者,因为它只是从提供者那里接收通用格式的数据,而它故意对. 因此,它不会关心特定于 JSON 的转换器。

然而,它会关心更通用的转换器来处理它的通用数据,幸运的是 .NET 已经有内置的基础设施:类型转换器。它们几乎从一开始就在 .NET 中使用,虽然它们很旧,但它们非常简单、可维护,并且确实非常适合这种特定场景。

如何实现类型转换器的完整示例超出了本答案的范围,但本质是您从 派生TypeConverter、覆盖适当的方法,并通过TypeConverterAttribute指向回您的TypeConverter实现来装饰要转换的类。那么它应该都是 Just Work™。


您提供的示例的警告是您实际上并没有尝试转换任何内容,而是尝试转换字符串,显然 aTypeConverter不会被调用,因为来自配置提供程序的源值是一个字符串,而选项类上的目标类型也是一个字符串。

您可以做的是创建一个新类来包装字符串以强制其大写:

public class UppercaseString
{
    public string Value { get; }

    public UppercaseString(string str)
    {
        Value = str.ToUpper();
    }

    public static implicit operator string(UppercaseString upper)
        => upper.Value;
}
Run Code Online (Sandbox Code Playgroud)

然后更改您的选项类以使用该包装器:

public class MyOptions
{
    public const string Name = "MyOptions";

    public UppercaseString MyString { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

最后,实现TypeConverterstring到 的转换UppercaseString

需要注意的定义implicit operator string-这可以让你使用的UppercaseString任何地方标准string的预期,这样你就不必更改您的代码,它引用MyOptions.MyStringMyOptions.MyString.Value