以下是我的代码:

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相关问答推荐

是否可以将gltf转换为字节数组,然后将字节数组转换回文件?

List T.AddRange在传递ConcurrentDictionary作为参数时引发ArgumentExcellent

ASP.NET MVC中创建视图的过滤器

. NET WireMock拒绝PostAsJsonAsync序列化

Microsoft. VisualBasic. FileIO. FileSystem. MoveFile()对话框有错误?

在LINQ Where子句中使用新的DateTime

可为空的泛型属性

S能够用DATETIME来计算,这有什么错呢?

注册所有IMediatR类

.NET SDK包中的官方C#编译器在哪里?

用于管理System.Text.Json中的多态反序列化的自定义TypeInfoResolver

避免只读记录 struct 中的防御副本

当使用Dapper映射DBNull时,我可以抛出异常吗?

实体框架-IsRequired()与OnDelete()

解决方案:延长ABP框架和ANGING OpenIddict中的令牌生命周期

C#中类库项目的源代码生成器

为什么Swashbakle/Swagger在参数中包含变量名?

当`JToken?`为空时?

如何在flutter dart中使用publicKey.xml文件进行rsa加密,我遇到了问题Exception:Could not parse BigInt

如何获取我在SQL中输入的值