【问题标题】:Asp.Net Core 3.1 Appsettings not respecting JsonConverterAsp.Net Core 3.1 Appsettings 不尊重 JsonConverter
【发布时间】:2021-05-23 20:57:27
【问题描述】:

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

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

    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());
    }

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

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

Startup.cs 更改以准备一切:

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

            services.Configure<MyOptions>(Configuration.GetSection(MyOptions.Name));
        }

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

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

【问题讨论】:

  • 他们不是故意的,也不是必须的。 JsonConverter 选项适用于 ASP.NET 请求和响应解析。 Microsoft.Extensions.Configuration 是一个单独的包,供所有 类型的项目(包括控制台项目)使用。 appsettings.json 不是一个特殊文件,它只是用于使用许多可用配置提供程序之一加载设置的常规名称。 JSON 提供程序使用文档中描述的特定格式加载设置。 所有设置,无论来源(json、ini、数据库、环境)如何,都存储为键/值对。
  • 为什么要使用 JsonConverter?您不能使用它来构造对象,这些对象是根据从所有提供程序加载的键/值对构造的。如果要将加载的值转换为大写 - 为什么?如果大小写很重要,您不应该保留大小写,还是确保存储 正确 大小写?如果不是,您可以对这些值使用不区分大小写的比较或不区分大小写的字典
  • 如果您想以特定方式加载设置,您可以创建自己的设置提供程序。我怀疑真正的问题是需要大写
  • 如果您需要大写设置,则需要将转换应用于所有设置,无论来源如何。这意味着您应该在绑定期间转换值,而不是在从某个特定提供程序加载期间。更好的是,为什么不更改MyString 的设置器以将值存储为大写?
  • 我想我在这里寻找的具体答案是 JsonConfigurationProvider 从不调用JsonSerializer.Deserialize,因为正如您指出的那样,它不需要,它只是用于创建键值对。它调用JsonDocument.Parse 来代替这项工作。感谢您的帮助。

标签: c# asp.net-core .net-core application-settings jsonconverter


【解决方案1】:

了解options pattern 实现为两个独立的步骤很重要:从配置源读取数据,然后将该数据绑定到强类型对象。

读取步骤由各种配置提供程序实现,其中只有一种是 JSON。您可能期望 JSON 提供程序会尊重您的 JsonConverter,但此步骤仅将其配置数据转换为下一步可接受的通用格式。

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

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

如何实现类型转换器的完整示例超出了此答案的范围,但要点是您从 TypeConverter 派生,覆盖适当的方法,并用 @ 装饰要转换的类987654323@ 指向您的 TypeConverter 实现。那么这一切都应该是 Just Work™。


您提供的示例需要注意的是,您实际上并没有尝试 convert 任何东西,而是尝试 transform 一个字符串,显然是TypeConverter 不会被调用,因为来自配置提供程序的源值是一个字符串,而您的选项类的目标类型也是一个字符串。

你可以做的是创建一个新类,它包装一个字符串以强制它为大写:

public class UppercaseString
{
    public string Value { get; }

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

    public static implicit operator string(UppercaseString upper)
        => upper.Value;
}

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

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

    public UppercaseString MyString { get; set; }
}

最后,实现一个从string 转换为UppercaseStringTypeConverter

注意implicit operator string 的定义 - 这允许您在任何需要标准string 的地方使用UppercaseString,这样您就不必将引用MyOptions.MyString 的代码更改为MyOptions.MyString.Value

【讨论】:

  • 价值包装的想法是惊人的。非常感谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-11-18
  • 1970-01-01
  • 2020-04-08
  • 2021-01-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多