我有一个用于配置的 toml 文件,其中几个部分包含我希望自动解析的 Crontab 表达式,而不是将其捕获为字符串然后解析它。
public record TestConfig(NCrontab.CrontabSchedule Schedule);
_builder.ConfigureAppConfiguration(options =>
{
options.AddTomlFile("Config.toml");
options.AddTomlFile("Config.Develop.toml");
});
_builder.ConfigureServices((ctx, services) =>
{
var test = ctx.Configuration.GetSection(nameof(TestConfig)).Get<TestConfig>() ?? throw new Exception();
}
我尝试过使用自定义
TypeConverter
但它似乎无法选择预期类型,因此所有字符串值都会通过此转换器,因此不是 crontab 的值只会转换为 null
public class CronTabConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
{
return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
}
public override object? ConvertFrom(ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object value)
{
if (value is null)
{
return null;
}
if (value is string strValue)
{
return CrontabSchedule.TryParse(strValue);
}
return base.ConvertFrom(context, culture, value);
}
public static void RegisterTypeConverter()
{
TypeDescriptor.AddAttributes(typeof(string), new TypeConverterAttribute(typeof(CronTabConverter)));
}
}
我可以看到这可能通过 JsonConverters 实现,但我不知道如何为
AddJsonFile
注册自定义转换器。
如果可以的话,我不反对将配置文件切换为 Json。
您的问题与 cron 无关,因此没有必要涉及此问题。与配置相同的故事。您的问题是自定义 JSON 序列化,这里重要的是提及您使用哪一个。
这是我的序列化器工厂,例如:
public static class TextOptionsFactory
{
// https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/migrate-from-newtonsoft?pivots=dotnet-7-0
public static void CustomizeJsonOptions(JsonSerializerOptions options)
{
// https://github.com/dotnet/runtime/issues/70352
options.IncludeFields = true;
options.ReferenceHandler = ReferenceHandler.IgnoreCycles;
options.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All);
options.PropertyNamingPolicy = null; //JsonNamingPolicy.CamelCase;
options.Converters.Add(new OptionConverterFactory());
options.Converters.Add(new ResultConverterFactory());
options.Converters.Add(new JsonStringEnumConverter());
options.Converters.Add(new LanguageIdentifierConverter());
options.Converters.Add(new WordIdentifierConverter());
options.Converters.Add(new TranslationIdentifierConverter());
options.Converters.Add(new CodeConverter());
options.Converters.Add(new LocalIdConverter());
}
public static JsonSerializerOptions DisableRequiredConstraint(this JsonSerializerOptions options)
{
// https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/required-properties
options.TypeInfoResolver = new DefaultJsonTypeInfoResolver
{
Modifiers =
{
static typeInfo =>
{
if (typeInfo.Kind != JsonTypeInfoKind.Object)
return;
foreach (JsonPropertyInfo propertyInfo in typeInfo.Properties)
{
// Strip IsRequired constraint from every property.
propertyInfo.IsRequired = false;
}
}
}
};
return options;
}
public static JsonSerializerOptions BuildJsonOptions(bool compact)
{
var options = new JsonSerializerOptions
{
WriteIndented = !compact,
};
CustomizeJsonOptions(options);
return options;
}
}
转换器之一:
public sealed class WordIdentifierConverter : JsonConverter<WordId>
{
public override WordId Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options) =>
new WordId(reader.GetInt32());
public override void Write(
Utf8JsonWriter writer,
WordId wordId,
JsonSerializerOptions options) =>
writer.WriteNumberValue(wordId.Id);
public override void WriteAsPropertyName(
Utf8JsonWriter writer,
WordId wordId,
JsonSerializerOptions options) =>
writer.WritePropertyName(wordId.Id.ToString());
public override WordId ReadAsPropertyName(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options) =>
new WordId(reader.GetInt32());
}
并进行设置,具体取决于您何时需要 - 我发现您需要它来读取文件,我还没有准备好使用示例,但您应该找到与这里类似的内容:
var mvc_builder = builder.Services.AddControllers();
mvc_builder.AddJsonOptions(json => TextOptionsFactory.CustomizeJsonOptions(json.JsonSerializerOptions));