You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 04:09:29