如何为ASP.NET配置系统创建自定义类型序列化器?
问题
希望创建自定义TimeSpan类型CustomTimeSpan,使其在ASP.NET配置(如appsettings.json)中保持与原生TimeSpan一致的序列化行为。原生配置写法示例:
"TimeSpanProperty":"01:30:45"
需要自定义类型支持同样的写法:
"CustomTimeSpanProperty":"02:30:45"
CustomTimeSpan初始实现:
public readonly struct CustomTimeSpan { private readonly TimeSpan _timeSpan; public readonly TimeSpan Value => _timeSpan; public CustomTimeSpan(TimeSpan timeSpan) { _timeSpan = timeSpan; } // Implicit operator to convert to total milliseconds public static implicit operator long(CustomTimeSpan customTimeSpan) { return (long)customTimeSpan.Value.TotalMilliseconds; } }
已实现基于JsonConverter的自定义转换器,但通过AddJsonOptions注册仅适用于控制器,无法让配置提供者使用该转换器,需了解如何自定义配置序列化逻辑。
解决方案
ASP.NET配置系统(如读取appsettings.json)依赖类型转换机制而非普通JSON序列化,因此不能直接复用JsonConverter。以下两种方案可实现需求:
方法一:实现全局TypeConverter
为CustomTimeSpan编写类型转换器,让配置系统自动识别并完成字符串与自定义类型的转换:
- 编写
TypeConverter子类:
using System.ComponentModel; using System.Globalization; public class CustomTimeSpanConverter : TypeConverter { public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) { return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType); } public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) { if (value is string strValue) { if (TimeSpan.TryParse(strValue, out var timeSpan)) { return new CustomTimeSpan(timeSpan); } throw new ArgumentException($"无法将字符串\"{strValue}\"转换为CustomTimeSpan类型"); } return base.ConvertFrom(context, culture, value); } public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) { return destinationType == typeof(string) || base.CanConvertTo(context, destinationType); } public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType == typeof(string) && value is CustomTimeSpan customTimeSpan) { return customTimeSpan.Value.ToString(@"hh:mm:ss.fff"); } return base.ConvertTo(context, culture, value, destinationType); } }
- 为
CustomTimeSpan添加特性标记:
[TypeConverter(typeof(CustomTimeSpanConverter))] public readonly struct CustomTimeSpan { // 原有实现保持不变 private readonly TimeSpan _timeSpan; public readonly TimeSpan Value => _timeSpan; public CustomTimeSpan(TimeSpan timeSpan) { _timeSpan = timeSpan; } public static implicit operator long(CustomTimeSpan customTimeSpan) { return (long)customTimeSpan.Value.TotalMilliseconds; } }
完成后,配置系统在绑定CustomTimeSpan类型属性时会自动使用该转换器,无需额外注册操作。
方法二:配置绑定阶段指定自定义转换器(.NET 6+)
如果不需要全局生效,可在配置绑定过程中单独指定转换器:
- 实现
IConfigurationConverter<CustomTimeSpan>接口:
using Microsoft.Extensions.Configuration; public class CustomTimeSpanConfigConverter : IConfigurationConverter<CustomTimeSpan> { public CustomTimeSpan Convert(object? input) { if (input is string strValue) { if (TimeSpan.TryParse(strValue, out var timeSpan)) { return new CustomTimeSpan(timeSpan); } throw new InvalidOperationException($"无效的CustomTimeSpan格式:{strValue}"); } throw new InvalidOperationException($"无法将类型{input?.GetType().Name}转换为CustomTimeSpan"); } }
- 绑定配置时注册转换器:
var config = new ConfigurationBuilder() .AddJsonFile("appsettings.json") .Build(); var options = new YourOptions(); config.Bind(options, binderOptions => { binderOptions.ConvertUsing(new CustomTimeSpanConfigConverter()); });
该方案灵活性更高,适合仅在特定配置绑定场景中使用自定义转换逻辑的需求。
内容的提问来源于stack exchange,提问作者mshwf
相关产品推荐
相关产品推荐

