以下是我的代码:

public partial class MainWindow : Window
{        
    public MainWindow()
    {
        InitializeComponent();
        Debug.WriteLine(JsonSerializer.Serialize<TestModel>(new TestModel() { Name = "123", Time = DateTime.Now }));
        Debug.WriteLine(JsonSerializer.Serialize<TestModelWithGeneric<int>>(new TestModelWithGeneric<int>() { Name = "123", Time = DateTime.Now ,Value=1}));
        Debug.WriteLine(JsonSerializer.Serialize<TestModelWithGeneric<string>>(new TestModelWithGeneric<string>() { Name = "123", Time = DateTime.Now, Value = "123" }));
    }
    [JsonDerivedType(typeof(TestModel), typeDiscriminator: "Base")]
    [JsonDerivedType(typeof(TestModelWithGeneric<int>), typeDiscriminator: "TestModelWithInt")]
    [JsonDerivedType(typeof(TestModelWithGeneric<string>), typeDiscriminator: "TestModelWithString")]
    public class TestModel {
        public string Name{ get; set; }
        public DateTime Time { get; set; }
    }
    public class TestModelWithGeneric<T> :TestModel{ 
        public T Value { get; set; }
    }
}   

输出为:

{"$type":"Base","Name":"123","Time":"2023-12-04T15:29:49.7867248+08:00"}
{"Value":1,"Name":"123","Time":"2023-12-04T15:29:49.8314012+08:00"}
{"Value":"123","Name":"123","Time":"2023-12-04T15:29:49.8354174+08:00"}

正如您所看到的,当存在泛型类型时,它总是缺少JsonDerivedType不起作用的$type.

我的代码有什么问题?

我使用的框架是.Net8.

推荐答案

TestModelWithGeneric<T>没有添加任何多态序列化信息,因此当序列化的泛型类型解析为TestModelWithGeneric<T>(泛型类型在编译时解析)时,System.Text.Json序列化程序将不会应用它.您可以显式指定类型:

Console.WriteLine(JsonSerializer.Serialize<TestModel>(new TestModel() { Name = "123", Time = DateTime.Now }));
Console.WriteLine(JsonSerializer.Serialize<TestModel>(new TestModelWithGeneric<int>() { Name = "123", Time = DateTime.Now ,Value=1}));
Console.WriteLine(JsonSerializer.Serialize<TestModel>(new TestModelWithGeneric<string>() { Name = "123", Time = DateTime.Now, 
     Value = "123" }));

但这并不是理想的 Select ,因为它很容易错过呼叫(例如,呼叫JsonSerializer.Serialize(new TestModelWithGeneric<string>() ...)将推断出TestModelWithGeneric<string>的类型不正确).另一种 Select 是使用定制IJsonTypeInfoResolver.例如,简单的一个可能看起来像(句柄只有TestModel个,但可以扩展为更通用的处理):

public class JsonHierarchyTypeInfoResolver : DefaultJsonTypeInfoResolver
{
    public override JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options)
    {
        var result = base.GetTypeInfo(type, options);
        if (type != typeof(TestModel) && type.IsAssignableTo(typeof(TestModel)))
        {
            var baseInfo = base.GetTypeInfo(typeof(TestModel), options);
            foreach (var derivedType in baseInfo.PolymorphismOptions.DerivedTypes)
            {
                if (derivedType.DerivedType.IsAssignableTo(type))
                {
                    result.PolymorphismOptions ??= new JsonPolymorphismOptions();
                    result.PolymorphismOptions.DerivedTypes.Add(derivedType);
                }
            }
        }

        return result;
    }
}

和用法:

var options = new JsonSerializerOptions
{
    TypeInfoResolver = new JsonHierarchyTypeInfoResolver()
};
Console.WriteLine(JsonSerializer.Serialize(
    new TestModel { Name = "123", Time = DateTime.Now }
    , options));
Console.WriteLine(JsonSerializer.Serialize(
    new TestModelWithGeneric<int> { Name = "123", Time = DateTime.Now ,Value=1}
    , options));
Console.WriteLine(JsonSerializer.Serialize(
    new TestModelWithGeneric<string> { Name = "123", Time = DateTime.Now, Value = "123" }
    , options));

这将导致:

{"$type":"Base","Name":"123","Time":"2023-12-04T12:37:43.9072465+03:00"}
{"$type":"TestModelWithInt","Value":1,"Name":"123","Time":"2023-12-04T12:37:44.1239771+03:00"}       
{"$type":"TestModelWithString","Value":"123","Name":"123","Time":"2023-12-04T12:37:44.1282147+03:00"}

另见:

Csharp相关问答推荐

在.NET核心项目中创建Startup.cs比在Program.cs中注册服务好吗?

我想在文本框悬停时在其底部显示一条线

MigraDoc文档

WinForms在Linux上的JetBrains Rider中的应用

try 使用C#ASP.NET收集WMI信息时访问被拒绝,但在PowerShell中工作

什么时候接受(等待)信号灯?尽可能的本地化?

如何从另一个类的列表中按ID取值

.NET8Blazor-为什么Rapzor渲染在for循环之后显示?

如何使用.NET Aspire从Blazor应用程序与GRPC API通信?

C#Microsoft.CodeAnalysis.CSharp.Scriiting不等待并行.对于

如何从SignalR获取连接客户端的域

ASP.NET Core 8 Web API:如何添加版本控制?

如何在C#中反序列化Java持续时间?

为什么INTEGER在通过反射调用时对空对象返回TRUE,而在INTEGER上调用时返回FALSE?

分别切换用于读取和写入的EF核心日志(log)

如何查找Span;T&>是否包含相同顺序的其他Span<;T&>

为什么.NET核心数学方法重复?

当反序列化json字符串时,它不会';t包括数据

LogTrace和LogDebug未出现在控制台中或调试Windows-.NET 7.0中的日志(log)记录问题

HttpClient 和服务器证书验证