C#运行时未知类型对象的JSON序列化与反序列化方案咨询
解决方案:动态类型的JSON序列化与反序列化
一、JSON序列化/反序列化的实现
完全可以实现,核心是序列化时嵌入类型信息,反序列化时依据类型信息还原对应实例。以下是两种常用JSON库的实现方式:
1. 使用Newtonsoft.Json(Json.NET)
序列化
开启TypeNameHandling自动嵌入类型标识,确保反序列化时能识别具体实现类:
// 假设已通过动态类型创建了IBrain实例 IBrain instance = Activator.CreateInstance(targetBrainType) as IBrain; string json = JsonConvert.SerializeObject(instance, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto, Formatting = Formatting.Indented }); // 将json写入文件或其他存储介质
反序列化
可通过自动识别类型或指定目标类型两种方式还原实例:
// 读取存储的json字符串 string json = File.ReadAllText("brain_config.json"); // 方式1:自动识别类型(依赖序列化时的TypeNameHandling配置) IBrain loadedInstance = JsonConvert.DeserializeObject<IBrain>(json, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto }); // 方式2:已知目标类型索引时,直接指定类型反序列化 Type[] brainTypes = typeof(Brains).GetNestedTypes().Where(b => b.GetInterface(nameof(IBrain)) != null).ToArray(); int targetIndex = 0; // 你选定的类型索引 IBrain loadedInstance2 = (IBrain)JsonConvert.DeserializeObject(json, brainTypes[targetIndex]);
2. 使用System.Text.Json
System.Text.Json默认不支持多态序列化,需自定义转换器来处理类型识别:
自定义多态转换器
public class BrainConverter : JsonConverter<IBrain> { private readonly Type[] _brainTypes; public BrainConverter(Type[] brainTypes) { _brainTypes = brainTypes; } public override IBrain Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { using (JsonDocument doc = JsonDocument.ParseValue(ref reader)) { if (!doc.RootElement.TryGetProperty("Type", out JsonElement typeElement)) throw new JsonException("JSON中缺少Type标识字段"); string typeName = typeElement.GetString(); Type targetType = _brainTypes.First(t => t.Name == typeName); return (IBrain)JsonSerializer.Deserialize(doc.RootElement.GetRawText(), targetType, options); } } public override void Write(Utf8JsonWriter writer, IBrain value, JsonSerializerOptions options) { writer.WriteStartObject(); // 写入当前实例的类型名称作为标识 writer.WriteString("Type", value.GetType().Name); // 序列化实例的其他属性 JsonSerializer.Serialize(writer, value, value.GetType(), options); writer.WriteEndObject(); } }
序列化与反序列化示例
Type[] brainTypes = typeof(Brains).GetNestedTypes().Where(b => b.GetInterface(nameof(IBrain)) != null).ToArray(); var options = new JsonSerializerOptions { Converters = { new BrainConverter(brainTypes) }, WriteIndented = true }; // 序列化 IBrain instance = Activator.CreateInstance(brainTypes[0]) as IBrain; string json = JsonSerializer.Serialize(instance, options); // 反序列化 IBrain loadedInstance = JsonSerializer.Deserialize<IBrain>(json, options);
二、其他替代方案
1. 二进制序列化
若不需要JSON的可读性,可选择二进制序列化(需类标记[Serializable],.NET Core/.NET 5+需引用System.Runtime.Serialization.Formatters.Binary):
// 序列化 IBrain instance = Activator.CreateInstance(brainTypes[0]) as IBrain; using (FileStream stream = new FileStream("brain_data.bin", FileMode.Create)) { BinaryFormatter formatter = new BinaryFormatter(); formatter.Serialize(stream, instance); } // 反序列化 using (FileStream stream = new FileStream("brain_data.bin", FileMode.Open)) { BinaryFormatter formatter = new BinaryFormatter(); IBrain loadedInstance = (IBrain)formatter.Deserialize(stream); }
注意:二进制序列化版本兼容性差,类结构变更可能导致反序列化失败。
2. 自定义读写函数
若类结构简单,可给IBrain接口添加抽象读写方法,由每个子类实现:
public interface IBrain { void SaveToFile(string path); void LoadFromFile(string path); } // 子类示例 public class Brains { [Serializable] public class BasicBrain : IBrain { public int CalculationCount { get; set; } public string LastResult { get; set; } public void SaveToFile(string path) { using (var writer = new StreamWriter(path)) { writer.WriteLine(CalculationCount); writer.WriteLine(LastResult); } } public void LoadFromFile(string path) { using (var reader = new StreamReader(path)) { CalculationCount = int.Parse(reader.ReadLine()); LastResult = reader.ReadLine(); } } } }
这种方式灵活性最高,但需手动处理每个字段的读写,适合字段较少的场景。
三、方案选择建议
- 优先选JSON序列化:可读性强、跨平台、兼容性好,推荐用Newtonsoft.Json(多态支持更便捷)。
- 追求性能或不需要可读性时,选二进制序列化。
- 类结构固定且字段少,或需要极致定制化逻辑时,选自定义读写函数。
内容的提问来源于stack exchange,提问作者Ioan
相关产品推荐
相关产品推荐

