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

protobuf-net跨框架反序列化异常:.NET Core序列化Type在.NET Framework中为null

.NET Core序列化含Type类型的类,在.NET Framework反序列化时Type变量为null

在.NET Core应用中序列化自定义类后,在完整.NET Framework应用中加载时,所有Type类型的变量反序列化后均为null。

演示代码

public class protobufTest
{
    string framework;

    public protobufTest()
    {
        string writeToFile1 = @"C:\temp\testClassNetFramework.pbf", writeToFile2 = @"C:\temp\testClassNET.pbf";
        framework = Assembly.GetEntryAssembly()?.GetCustomAttribute<TargetFrameworkAttribute>()?.FrameworkName;
        Console.WriteLine("Current framework is " + framework);
        string writeToFile;
        if (framework.Contains(".NETFramework", StringComparison.OrdinalIgnoreCase)) writeToFile = writeToFile1;
       else if (framework.Contains(".NETCoreApp", StringComparison.OrdinalIgnoreCase)) writeToFile = writeToFile2;
        else throw new Exception("haven't allowed for framework " + framework);
        var tc = make();

        write(tc, writeToFile);
        Console.WriteLine($"\n\nwritten to: {writeToFile}\n{tc.ToString()}");

        if (File.Exists(writeToFile1))
        {
            var tc1 = Load(writeToFile1);
            Console.WriteLine($"\n\nread from: {writeToFile1}\n{tc1.ToString()}");
        }

        if (File.Exists(writeToFile2))
        {
            var tc2 = Load(writeToFile2);
            Console.WriteLine($"\n\nread from: {writeToFile2}\n{tc2.ToString()}");
        }
    }

    testClass make()
    {
        var tc = new testClass();
        tc.frameworkWhenSaved = framework;
        tc.aType = typeof(long);
        tc.types.Add(typeof(int));
        tc.types.Add(typeof(char));
        return tc;
    }

    testClass Load(string fullFileName)
    {
        MemoryStream ms;

        using (FileStream file = new FileStream(fullFileName, FileMode.Open, FileAccess.Read))
        {
            byte[] bytes = new byte[file.Length];
            file.Read(bytes, 0, (int)file.Length);
            ms = new MemoryStream(bytes);
        }

        ms.Position = 0;
        testClass tc = Serializer.Deserialize<testClass>(ms);

        return tc;
    }

    void write(testClass tc, string fullFileName)
    {
        MemoryStream ms = new MemoryStream();
        Serializer.Serialize<testClass>(ms, tc);
        ms.Position = 0;

        using (FileStream file = new FileStream(fullFileName, FileMode.Create, System.IO.FileAccess.Write))
        {
            byte[] bytes = new byte[ms.Length];
            ms.Read(bytes, 0, (int)ms.Length);
            file.Write(bytes, 0, bytes.Length);
            ms.Close();
        }
    }
}

运行结果

.NET Core运行时输出(符合预期)

Current framework is .NETCoreApp,Version=v7.0

written to: C:\temp\testClassNET.pbf testClass created by .NETCoreApp,Version=v7.0 aType=System.Int64 types=System.Int32,System.Char,

read from: C:\temp\testClassNetFramework.pbf testClass created by .NETFramework,Version=v4.7.2 aType=System.Int64 types=System.Int32,System.Char,

read from: C:\temp\testClassNET.pbf testClass created by .NETCoreApp,Version=v7.0 aType=System.Int64 types=System.Int32,System.Char,

.NET Framework运行时输出(Type变量为null)

Current framework is .NETFramework,Version=v4.7.2

written to: C:\temp\testClassNetFramework.pbf testClass created by .NETFramework,Version=v4.7.2 aType=System.Int64      types=System.Int32,System.Char,

read from: C:\temp\testClassNetFramework.pbf testClass created by .NETFramework,Version=v4.7.2 aType=System.Int64    
types=System.Int32,System.Char,

read from: C:\temp\testClassNET.pbf testClass created by .NETCoreApp,Version=v7.0 aType= types=null,null,

(需先在两个框架中各运行一次生成文件)


问题原因

这是因为.NET Core(及.NET 5+)和.NET Framework中Type类型的序列化格式、底层程序集命名存在差异:

  1. Protobuf-NET默认序列化Type时,.NET Core使用的是简化的程序集标识(比如依赖System.Runtime),而.NET Framework依赖的是mscorlib,两者的类型标识无法直接兼容。
  2. 跨框架时,Protobuf-NET无法识别对方生成的Type序列化标识,导致类型解析失败,最终返回null。

解决方案

自定义Type的序列化/反序列化逻辑,统一跨框架的类型标识格式:

1. 实现Type的代理类(Surrogate)

public class TypeSurrogate
{
    public string TypeName { get; set; }

    // 从代理类转换为Type
    public static implicit operator Type(TypeSurrogate surrogate)
    {
        if (string.IsNullOrEmpty(surrogate.TypeName)) return null;
        
        // 先尝试直接解析
        var type = Type.GetType(surrogate.TypeName, false);
        if (type != null) return type;

        // 兼容.NET Core和.NET Framework的程序集差异
        var adjustedName = surrogate.TypeName
            .Replace("System.Runtime, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", 
                     "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
        return Type.GetType(adjustedName, true);
    }

    // 从Type转换为代理类
    public static implicit operator TypeSurrogate(Type type)
    {
        if (type == null) return null;
        // 存储类型的完全限定名+程序集信息
        return new TypeSurrogate { TypeName = type.AssemblyQualifiedName };
    }
}

2. 注册代理类到Protobuf-NET

在序列化/反序列化操作前,注册自定义代理:

RuntimeTypeModel.Default.Add(typeof(Type), false).SetSurrogate(typeof(TypeSurrogate));

3. 确保testClass的字段配置正确

为testClass中的Type字段添加[ProtoMember]标记,确保Protobuf-NET能识别并使用代理类处理。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 00:32:17