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

使用System.Text.Json序列化与反序列化含IElement派生类的Form对象问题

解决System.Text.Json序列化/反序列化接口派生类丢失字段的问题

你的问题核心是System.Text.Json默认不支持多态序列化,而且默认只序列化属性(不处理字段),所以才会丢失派生类的额外成员。下面分序列化和反序列化两步来解决:

一、先修正代码里的小问题

首先注意到你Radiobutton类里的字段拼写是Choises,但实例化时用的是Choices,这会导致编译错误,先把它改成一致的Choices;另外建议把字段改成属性(System.Text.Json默认不序列化字段,改成属性更规范):

public class Radiobutton : IElement 
{ 
    [JsonPropertyName("type")] 
    public string? Type { get; set; } 
    [JsonPropertyName("name")] 
    public string? Name { get; set; } 
    [JsonPropertyName("choices")] 
    public List<string> Choices { get; set; } = new List<string>(); // 改成属性,拼写修正
}

二、配置多态序列化,保留所有派生类成员

System.Text.Json需要明确知道接口的派生类型,才能序列化所有成员。我们可以通过特性标记的方式来配置:

1. 给接口添加多态标记

在IElement接口上添加JsonPolymorphism和JsonDerivedType特性,指定类型鉴别符的键名(这里用你已经定义的type字段)和对应的派生类:

[JsonPolymorphism(TypeDiscriminatorPropertyName = "type")] // 指定用"type"字段作为类型鉴别符
[JsonDerivedType(typeof(Textbox), typeDiscriminator: "Textbox")]
[JsonDerivedType(typeof(Radiobutton), typeDiscriminator: "Radiobutton")]
public interface IElement 
{ 
    string? Type { get; set; } 
    string? Name { get; set; } 
}

2. 序列化时配置选项

如果你的代码里还有字段需要序列化(比如没改成属性的情况),要在JsonSerializerOptions里开启IncludeFields;另外可以开启WriteIndented让JSON更易读:

var serializeOptions = new JsonSerializerOptions
{
    IncludeFields = true, // 如果你有需要序列化的字段,就开启这个;如果全是属性可以去掉
    WriteIndented = true,
    PropertyNameCaseInsensitive = true, // 反序列化时有用,大小写不敏感
};

// 执行序列化
string json = JsonSerializer.Serialize(form, serializeOptions);

此时序列化出来的JSON会包含所有派生类的成员:

{
  "elements": [
    {
      "type": "Textbox",
      "name": "txt1",
      "text": "One"
    },
    {
      "type": "Radiobutton",
      "name": "radio1",
      "choices": [
        "One",
        "Two",
        "Three"
      ]
    }
  ]
}

三、反序列化JSON到Form类

反序列化时使用和序列化完全相同的配置选项,System.Text.Json会根据type字段自动推断对应的派生类:

Form deserializedForm = JsonSerializer.Deserialize<Form>(json, serializeOptions);

// 验证反序列化结果
foreach (var element in deserializedForm.Elements)
{
    if (element is Textbox txt)
        Console.WriteLine($"Textbox: {txt.Text}");
    else if (element is Radiobutton radio)
        Console.WriteLine($"Radiobutton choices: {string.Join(", ", radio.Choices)}");
}

额外优化建议

你可以在派生类的构造函数里自动设置Type属性,避免手动赋值:

public class Textbox : IElement 
{ 
    public Textbox() => Type = "Textbox";
    
    [JsonPropertyName("type")] 
    public string? Type { get; set; } 
    [JsonPropertyName("name")] 
    public string? Name { get; set; } 
    [JsonPropertyName("text")] 
    public string? Text { get; set; } 
}

public class Radiobutton : IElement 
{ 
    public Radiobutton() => Type = "Radiobutton";
    
    [JsonPropertyName("type")] 
    public string? Type { get; set; } 
    [JsonPropertyName("name")] 
    public string? Name { get; set; } 
    [JsonPropertyName("choices")] 
    public List<string> Choices { get; set; } = new List<string>(); 
}

这样实例化时就不用手动写Type = "Textbox"了,代码更简洁。

内容的提问来源于stack exchange,提问作者Enrico

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 18:17:40