如何使用System.Text.Json序列化含泛型接口参数的类?
解决System.Text.Json序列化泛型接口类型属性为空的问题
问题背景
定义了以下表示报表和图表的类:
public class ReportDTO<TChart> where TChart : IChart { public TChart ChartDTO { get; set; } public DateTime? From { get; set; } public DateTime? To { get; set; } public string Title { get; set; } = null!; public string? Subtitle { get; set; } public string? ChartTitle { get; set; } } public class ApexChartDTO : IChart { public Chart? chart { get; set; } = null!; public DataLabels? dataLabels { get; set; } public Fill? fill { get; set; } public Stroke? stroke { get; set; } public Tooltip? tooltip { get; set; } public Grid? grid { get; set; } public Xaxis? xaxis { get; set; } public Yaxis? yaxis { get; set; } public string[]? labels { get; set; } public string[]? colors { get; set; } public Legend? legend { get; set; } } public class PieChartDTO : ApexChartDTO, IChart { public PieChartDTO(int[] Series, string[] Labels) { // 初始化逻辑 } public int[]? series { get; set; } = new int[0]; public Plotoptions? plotOptions { get; set; } }
当序列化泛型类型为IChart的ReportDTO<IChart>实例时,实际类型为PieChartDTO的ChartDTO属性被序列化为空对象:
DateTime from = new DateTime(2022, 02, 13); DateTime to = new DateTime(2022, 02, 19); ReportDTO<IChart> pie = new ReportDTO<IChart>() { Title = "My chart Title", From = from, To = to, Subtitle = "This is a Subtitle", ChartTitle = $"Data from {from:dd.MM.yyyy} to {to:dd.MM.yyyy}", ChartDTO = new PieChartDTO(new int[] { 0, 3, 4, 2 }, new string[] { "label1", "label2", "label3", "label4" }) }; string json = JsonSerializer.Serialize(pie);
序列化结果:
{ "ChartDTO": {}, "From": "2023-02-13T00:00:00", "To": "2023-02-19T00:00:00", "Title": "My chart Title", "Subtitle": "This is a Subtitle", "ChartTitle": "Data from 13.02.2023 to 19.02.2023" }
原因分析
System.Text.Json默认仅序列化编译时类型的成员。ChartDTO的编译时类型是IChart(接口,无公开属性),即使运行时实例是PieChartDTO,序列化器也只会读取IChart定义的成员,因此输出空对象。
解决方法
方法1:通过特性声明派生类型
在IChart接口上添加JsonDerivedType特性,明确告知序列化器所有可能的派生类型:
[JsonDerivedType(typeof(ApexChartDTO))] [JsonDerivedType(typeof(PieChartDTO))] public interface IChart { }
添加后直接执行序列化,序列化器会自动识别运行时类型并序列化所有属性。
方法2:序列化时配置类型解析器
如果无法修改接口,可以在序列化时通过JsonSerializerOptions配置多态解析规则:
var options = new JsonSerializerOptions { TypeInfoResolver = new DefaultJsonTypeInfoResolver { Modifiers = { typeInfo => { if (typeInfo.Type == typeof(IChart)) { typeInfo.PolymorphismOptions = new JsonPolymorphismOptions { DerivedTypes = { new JsonDerivedType(typeof(ApexChartDTO)), new JsonDerivedType(typeof(PieChartDTO)) } }; } } } }, WriteIndented = true }; string json = JsonSerializer.Serialize(pie, options);
此方式会在JSON中添加$type字段标识具体类型,反序列化时也能正确还原实例。
方法3:使用具体泛型类型
如果业务场景允许,直接使用具体类型作为泛型参数,而非接口:
ReportDTO<PieChartDTO> pie = new ReportDTO<PieChartDTO>() { Title = "My chart Title", From = from, To = to, Subtitle = "This is a Subtitle", ChartTitle = $"Data from {from:dd.MM.yyyy} to {to:dd.MM.yyyy}", ChartDTO = new PieChartDTO(new int[] { 0, 3, 4, 2 }, new string[] { "label1", "label2", "label3", "label4" }) }; string json = JsonSerializer.Serialize(pie);
这种方式无需额外配置,序列化器会直接序列化PieChartDTO的所有属性。
内容的提问来源于stack exchange,提问作者GreeNSparT
相关产品推荐
相关产品推荐

