使用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
相关产品推荐
相关产品推荐

