迁移WCF至.NET Core 2.0:替换SoapFormatter并兼容旧序列化数据
关于SoapFormatter迁移到XmlSerializer的数据兼容问题
嘿,先给你明确结论:XmlSerializer没办法直接反序列化原来用SoapFormatter存的数据,这俩的序列化逻辑和输出格式差异太大了——
SoapFormatter是.NET Framework专属的,生成的SOAP里带了一堆.NET特有的类型元数据(比如__type属性),连私有成员都会被序列化进去;而XmlSerializer是基于标准XML的,只处理公共成员,格式更简洁,完全不依赖.NET的专属类型信息。直接替换的话,肯定读不出旧数据,甚至会抛出反序列化异常。
不过别慌,有几个靠谱的解决方案:
1. 用第三方库兼容SoapFormatter的旧数据
既然.NET Core 2.0没内置SoapFormatter,咱可以用第三方移植的库来读取旧数据,推荐两个高评分的选项:
选项A:SoapFormatter.NETCore(最贴近原代码)
这个包就是把.NET Framework里的System.Runtime.Serialization.Formatters.Soap移植到了.NET Core,用法和你原来的代码几乎一模一样,学习成本最低:
- 先装NuGet包:
Install-Package SoapFormatter.NETCore - 然后修改你的反序列化代码(只改创建Formatter的那行):
public UserQuestionAnswers Deserialize(byte[] buffer) { using (var stream = new MemoryStream(buffer)) { // 这里换成第三方库的SoapFormatter var formatter = new SoapFormatter(); var questionsAnswers = formatter.Deserialize(stream) as BusinessProcesses.Authentication.UserQuestionAnswers; if (questionsAnswers == null) return null; // 后面的映射逻辑完全不用改 var uQuestionAnswers = new UserQuestionAnswers { Domain = questionsAnswers.Domain, KeyLabel = questionsAnswers.KeyLabel, Username = questionsAnswers.Username, UserUid = questionsAnswers.UserUid, QuestionAnswers = (from item in questionsAnswers.QuestionAnswers where item != null select new QuestionAnswer { QuestionId = item.QuestionId, QuestionHash = item.QuestionHash, AnswerHmac = item.AnswerHMAC, QuestionText = string.Empty }).ToArray() }; return uQuestionAnswers; } }
选项B:ServiceStack.Text(跨平台更灵活)
ServiceStack.Text是个老牌的序列化库,支持跨平台的SOAP序列化,也能兼容SoapFormatter的格式:
- 装NuGet包:
Install-Package ServiceStack.Text - 代码调整成这样(把字节数组转成XML字符串再反序列化):
public UserQuestionAnswers Deserialize(byte[] buffer) { var xmlString = Encoding.UTF8.GetString(buffer); var questionsAnswers = ServiceStack.Text.XmlSerializer.Deserialize<BusinessProcesses.Authentication.UserQuestionAnswers>(xmlString); if (questionsAnswers == null) return null; // 映射逻辑照旧 var uQuestionAnswers = new UserQuestionAnswers { Domain = questionsAnswers.Domain, KeyLabel = questionsAnswers.KeyLabel, Username = questionsAnswers.Username, UserUid = questionsAnswers.UserUid, QuestionAnswers = (from item in questionsAnswers.QuestionAnswers where item != null select new QuestionAnswer { QuestionId = item.QuestionId, QuestionHash = item.QuestionHash, AnswerHmac = item.AnswerHMAC, QuestionText = string.Empty }).ToArray() }; return uQuestionAnswers; }
2. 混合序列化的过渡方案
如果你想逐步切换到XmlSerializer(比如新数据用XmlSerializer存,旧数据用第三方库读),可以这么做:
- 读取时:用上面的第三方库反序列化旧数据;
- 写入时:用XmlSerializer序列化新数据;
- 之后可以写个后台任务,把数据库里的旧数据批量转换成XmlSerializer格式,彻底完成迁移。
另外,用XmlSerializer的时候,记得给你的实体类加序列化特性,不然可能会出现格式问题,比如:
[XmlRoot("UserQuestionAnswers")] public class UserQuestionAnswers { [XmlElement("Domain")] public string Domain { get; set; } [XmlElement("KeyLabel")] public string KeyLabel { get; set; } [XmlElement("Username")] public string Username { get; set; } [XmlElement("UserUid")] public string UserUid { get; set; } [XmlArray("QuestionAnswers"), XmlArrayItem("QuestionAnswer")] public QuestionAnswer[] QuestionAnswers { get; set; } } [XmlRoot("QuestionAnswer")] public class QuestionAnswer { [XmlElement("QuestionId")] public int QuestionId { get; set; } [XmlElement("QuestionHash")] public string QuestionHash { get; set; } [XmlElement("AnswerHmac")] public string AnswerHmac { get; set; } [XmlElement("QuestionText")] public string QuestionText { get; set; } }
最后总结一下
- 直接替换成XmlSerializer读旧数据肯定不行,格式不兼容;
- 最快的解决办法是用第三方库兼容SoapFormatter;
- 要是想长期切换到XmlSerializer,就用混合策略过渡,之后批量迁移数据。
内容的提问来源于stack exchange,提问作者Dinesh
相关产品推荐
相关产品推荐

