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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 14:21:20