前言
.Net 6引入了DateOnly和TimeOnly结构,可以存储日期和时间。
但在实际使用时,发现一个很尴尬的问题,DateOnly和TimeOnly居然不能被序列化:
var builder = WebApplication.CreateBuilder(args);var app = builder.Build();app.MapGet("/timeonly", () => new TimeOnly(1,0,0) );
app.MapGet("/dateonly", () => new DateOnly(2021,12,01) );app.Run();
那要这两类型还有何用!
解决方案
自定义转换器
为了解决这个问题,我们需要创建两个自定义转换器来处理这些类型,以TimeOnly为例:
public class TimeOnlyConverter : JsonConverter<TimeOnly>
{public override TimeOnly Read(ref Utf8JsonReader reader,Type typeToConvert, JsonSerializerOptions options){var value = reader.GetString();return TimeOnly.Parse(value!);}public override void Write(Utf8JsonWriter writer, TimeOnly value,JsonSerializerOptions options)=> writer.WriteStringValue(value.ToString("HH:mm:ss"));
}
继承JsonConverter类,并实现Read
和Write
方法。
配置
现在,我们需要将自定义转换器配置到序列化选项上。
在.NET 6之前,是修改Startup.cs文件:
public void ConfigureServices(IServiceCollection services)
{services.AddControllers().AddJsonOptions(options =>options.JsonSerializerOptions.Converters.Add(new TimeOnlyConverter()));
}
但是在.NET 6中已经没有Startup.cs文件,也不用Controller了。
查看AddJsonOptions
方法的源码,发现它实际上调用的是builder.Services.Configure
方法:
public static IMvcCoreBuilder AddJsonOptions(this IMvcCoreBuilder builder,Action<JsonOptions> configure)
{if (builder == null){throw new ArgumentNullException(nameof(builder));}if (configure == null){throw new ArgumentNullException(nameof(configure));}builder.Services.Configure(configure);return builder;
}
于是,我们同样使用builder.Services.Configure
方法进行配置:
using Microsoft.AspNetCore.Http.Json;var builder = WebApplication.CreateBuilder(args);
builder.Services.Configure<JsonOptions>(options =>options.SerializerOptions.Converters.Add(new TimeOnlyConverter()));
运行结果正确:
结论
不清楚官方为什么默认不支持DateOnly和TimeOnly类型序列化。
如果你碰到类似本文这种情况,可以使用自定义转换器解决。
如果你觉得这篇文章对你有所启发,请关注我的个人公众号”My IO“