Asp.Net Core 3.1 Appsettings 不遵守 JsonConverter
Asp.Net Core 3.1 Appsettings not respecting JsonConverter
在 asp.net 核心 3.1 中,使用新的 System.Text.Json,我试图在应用程序设置部分使用自定义 JsonConverter。手动 serializing/deserializing 很好地尊重转换器,但通过选项模式从 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 class,在字符串上声明转换器 属性:
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<MyOptions>
注入 HomeController 时,它读取一个小写值。如果我手动执行 JsonSerializer.Deserialize<MyOptions>("{\"MyString\":\"lowercase_manual\"}")
,我会得到一个大写字符串。即使我删除了 JsonSerializerOptions 的启动声明。
有谁知道如何让应用程序设置/选项模式尊重 JsonConverter?我必须在其他地方声明 JsonSerializerOptions 吗?谢谢。
了解 options pattern 是作为两个独立步骤实现的,这一点很重要:从配置源读取数据,然后将该数据绑定到 strongly-typed 对象。
读取步骤由各种配置提供程序实现,其中只有一个是JSON。您可能希望 JSON 提供程序会尊重您的 JsonConverter
,但此步骤仅将其配置数据执行最少的转换,将其转换为下一步可以接受的通用格式。
那么,绑定步骤似乎是 JsonConverter
关心的地方。但是此步骤有意完全不了解任何特定的配置提供程序,因为它只是从提供程序 那里接收通用格式的数据,它故意对 一无所知。因此,它不会关心 JSON-specific 转换器。
但是,它会关心更多通用转换器来处理其通用数据,幸运的是 .NET 已经为此内置了基础结构:type converters。这些几乎从一开始就在 .NET 中,虽然它们已经过时,但它们非常简单、易于维护,而且确实是这种特定场景的理想选择。
关于如何实现类型转换器的完整示例超出了此答案的范围,但要点是您从 TypeConverter
派生,覆盖适当的方法,并装饰 class您希望使用指向您的 TypeConverter
实现的 TypeConverterAttribute
进行转换。那么它应该都是 Just Work™。
您提供的示例需要注意的是,您实际上并不是在尝试转换任何东西,您是在尝试转换 一个字符串,显然 TypeConverter
不会被调用,因为来自配置提供程序的源值是一个字符串,而您的选项 class 上的目标类型也是一个字符串。
你可以做的是创建一个新的 class 来包装一个字符串以强制它变成大写:
public class UppercaseString
{
public string Value { get; }
public UppercaseString(string str)
{
Value = str.ToUpper();
}
public static implicit operator string(UppercaseString upper)
=> upper.Value;
}
然后更改您的选项 class 以使用该包装器:
public class MyOptions
{
public const string Name = "MyOptions";
public UppercaseString MyString { get; set; }
}
最后,实现一个从 string
转换为 UppercaseString
的 TypeConverter
。
请注意 implicit operator string
的定义 - 这允许您在需要标准 string
的任何地方使用 UppercaseString
,这样您就不必更改引用的代码MyOptions.MyString
到 MyOptions.MyString.Value
.
在 asp.net 核心 3.1 中,使用新的 System.Text.Json,我试图在应用程序设置部分使用自定义 JsonConverter。手动 serializing/deserializing 很好地尊重转换器,但通过选项模式从 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 class,在字符串上声明转换器 属性:
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<MyOptions>
注入 HomeController 时,它读取一个小写值。如果我手动执行 JsonSerializer.Deserialize<MyOptions>("{\"MyString\":\"lowercase_manual\"}")
,我会得到一个大写字符串。即使我删除了 JsonSerializerOptions 的启动声明。
有谁知道如何让应用程序设置/选项模式尊重 JsonConverter?我必须在其他地方声明 JsonSerializerOptions 吗?谢谢。
了解 options pattern 是作为两个独立步骤实现的,这一点很重要:从配置源读取数据,然后将该数据绑定到 strongly-typed 对象。
读取步骤由各种配置提供程序实现,其中只有一个是JSON。您可能希望 JSON 提供程序会尊重您的 JsonConverter
,但此步骤仅将其配置数据执行最少的转换,将其转换为下一步可以接受的通用格式。
那么,绑定步骤似乎是 JsonConverter
关心的地方。但是此步骤有意完全不了解任何特定的配置提供程序,因为它只是从提供程序 那里接收通用格式的数据,它故意对 一无所知。因此,它不会关心 JSON-specific 转换器。
但是,它会关心更多通用转换器来处理其通用数据,幸运的是 .NET 已经为此内置了基础结构:type converters。这些几乎从一开始就在 .NET 中,虽然它们已经过时,但它们非常简单、易于维护,而且确实是这种特定场景的理想选择。
关于如何实现类型转换器的完整示例超出了此答案的范围,但要点是您从 TypeConverter
派生,覆盖适当的方法,并装饰 class您希望使用指向您的 TypeConverter
实现的 TypeConverterAttribute
进行转换。那么它应该都是 Just Work™。
您提供的示例需要注意的是,您实际上并不是在尝试转换任何东西,您是在尝试转换 一个字符串,显然 TypeConverter
不会被调用,因为来自配置提供程序的源值是一个字符串,而您的选项 class 上的目标类型也是一个字符串。
你可以做的是创建一个新的 class 来包装一个字符串以强制它变成大写:
public class UppercaseString
{
public string Value { get; }
public UppercaseString(string str)
{
Value = str.ToUpper();
}
public static implicit operator string(UppercaseString upper)
=> upper.Value;
}
然后更改您的选项 class 以使用该包装器:
public class MyOptions
{
public const string Name = "MyOptions";
public UppercaseString MyString { get; set; }
}
最后,实现一个从 string
转换为 UppercaseString
的 TypeConverter
。
请注意 implicit operator string
的定义 - 这允许您在需要标准 string
的任何地方使用 UppercaseString
,这样您就不必更改引用的代码MyOptions.MyString
到 MyOptions.MyString.Value
.