C#中反序列化Proto定义对象时遇TAR_GZ枚举转换错误
这个问题的核心原因是:C#的Protobuf代码生成器默认会将Proto定义中带下划线的枚举名称转换为PascalCase格式(比如TAR_GZ会被生成TarGz),但你的JSON使用的是Proto原始的下划线命名,两者无法匹配,导致反序列化失败。而TAR能正常工作是因为它是单单词,生成的Tar枚举成员可以被默认的字符串枚举转换器不区分大小写匹配。
下面提供几种可行的解决方法:
方法1:修改Proto文件,强制C#枚举保留下划线命名
在Proto文件的TAR_GZ枚举值上添加csharp_name选项,指定生成的C#枚举成员名称与Proto定义完全一致:
message CodeDependency { string path = 1; DependencyType type = 2; enum DependencyType { NONE = 0; TAR = 1; ZIP = 2; TAR_GZ = 3 [option csharp_name = "TAR_GZ"]; DIRECTORY = 4; } }
重新生成C#代码后,枚举成员就会是TAR_GZ,此时JSON中的"TAR_GZ"就能直接被Newtonsoft或Protobuf序列化器识别。
方法2:配置Newtonsoft.Json的枚举转换器(无需修改Proto)
如果无法修改Proto文件,可以通过Newtonsoft.Json的命名策略转换器,实现下划线枚举值到PascalCase枚举成员的自动映射:
using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Serialization; // 配置JSON序列化设置 var jsonSettings = new JsonSerializerSettings { Converters = new List<JsonConverter> { new StringEnumConverter(new SnakeCaseNamingStrategy()) { AllowIntegerValues = false, CamelCaseText = false } } }; // 反序列化你的JSON(假设根对象为RootObject) var result = JsonConvert.DeserializeObject<RootObject>(yourJsonString, jsonSettings);
SnakeCaseNamingStrategy会自动将JSON中的下划线格式(TAR_GZ)转换为C#枚举的PascalCase格式(TarGz),完美匹配Protobuf生成的枚举成员。
方法3:使用Protobuf官方的JSON解析器
注意:你之前尝试的CodeDependency.Parser.ParseFrom是用于解析二进制Protobuf数据的,并不适用于JSON。Protobuf官方提供了专门的JSON解析器Google.Protobuf.JsonParser,它会严格遵循Proto定义的命名规则,直接识别带下划线的枚举值:
using Google.Protobuf; using Google.Protobuf.Json; // 创建JSON解析器 var parser = new JsonParser(JsonParser.Settings.Default); // 直接解析JSON到CodeDependency对象 var codeDependency = parser.Parse<CodeDependency>(yourJsonString);
这种方法不需要额外配置,行为和Python中完全一致,因为官方解析器会直接使用Proto文件中定义的枚举名称(包括下划线)进行匹配。
如果使用的是protobuf-net库的ProtoBuf.Serializer,也可以给生成的枚举成员手动添加[ProtoEnum(Name = "TAR_GZ")]特性来指定对应的Proto名称,不过这种方式需要修改生成的C#代码(或者通过Proto文件的选项自动生成)。
内容的提问来源于stack exchange,提问作者user9345277

