【发布时间】:2021-01-19 18:08:21
【问题描述】:
所以我正在编写一个迁移工具来将一堆只是二进制格式对象的数据文件转换为我们漂亮的新 YAML 格式。问题是,从那时起,我们还对底层类进行了更改。所以我的计划是使用从源代码管理中获取的类的重命名版本来反序列化磁盘上的数据,将属性映射到新版本,然后将新版本序列化到 YAML。
所以“旧”类是这样的:
[Serializable]
public class OldQuestionnaire : INotifyPropertyChanged
{
private static Random random = new Random((int)DateTime.Now.Ticks);
private bool synchronised;
public OldQuestionnaire()
{
Questions = new List<Question>();
QuestionnaireInstanceID = random.Next(0, int.MaxValue);
}
[field: NonSerialized]
public event PropertyChangedEventHandler PropertyChanged;
public string Customer { get; set; }
public DateTime FilledDate { get; set; }
public string FSR { get; set; }
public bool IsPreferred { get; set; }
public bool IsSiteSpecific { get; set; }
public string JobRef { get; set; }
public int QuestionnaireInstanceID { get; }
public string QuestionnaireTemplateID { get; set; }
//Database template ID
public List<Question> Questions { get; set; }
public DateTime ReleaseDate { get; set; }
public string Site { get; set; }
public bool Synchronised
{
get => synchronised;
set
{
synchronised = value;
OnPropertyChanged();
}
}
public string TempStorageFilePath { get; set; }
public string Title { get; set; }
public void DeleteStoredCopy()
{
if (TempStorageFilePath == null || !File.Exists(TempStorageFilePath))
{
return;
}
File.Delete(TempStorageFilePath);
}
public OldQuestionnaire GetBlankCopy()
{
OldQuestionnaire copy = new OldQuestionnaire
{
Questions = Questions.Select(q => q.Clone()).Cast<Question>().ToList(),
Title = Title,
ReleaseDate = ReleaseDate,
QuestionnaireTemplateID = QuestionnaireTemplateID
};
return copy;
}
public void StoreToDisk(string tempPath)
{
string fullPath = Path.Combine(tempPath, $"{QuestionnaireInstanceID}.csat");
TempStorageFilePath = fullPath;
if (File.Exists(fullPath))
{
File.Delete(fullPath);
}
BinaryFormatter formatter = new BinaryFormatter();
using (Stream fStream = File.OpenWrite(fullPath))
{
formatter.Serialize(fStream, this);
}
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
注意,它以前只是被称为Questionnaire。
所以迁移过程应该很简单:
OldQuestionnaire oldQ;
using (StreamReader reader = new StreamReader("../../../libcsatmigrations/BinaryToYAML1/ReferenceFiles/1753324844.csat"))
{
BinaryFormatter formatter = new BinaryFormatter {Binder = new OldQBinder()};
oldQ = (OldQuestionnaire)formatter.Deserialize(reader.BaseStream);
}
BinaryToYAMLMigrator migrator = new BinaryToYAMLMigrator();
string yaml = migrator.Migrate(oldQ);
migrator.Migrate(oldQ) 在哪里处理属性映射等。
最初,这引发了BinaryFormatter 找不到OldQuestionnaire 所需程序集的问题,因此我创建了一个自定义活页夹:
public class OldQBinder:SerializationBinder
{
public override Type BindToType(string assemblyName, string typeName)
{
return typeName switch
{
"libcsatquestionnaire.Question" => typeof(Question),
"libcsatquestionnaire.Questionnaire" => typeof(OldQuestionnaire),
"libcsatquestionnaire.OneTenQuestion" => typeof(OneTenQuestion),
"libcsatquestionnaire.FreeCommentQuestion" => typeof(FreeCommentQuestion),
_ => null,
};
}
}
(顺便说一句,喜欢新的 C#8 模式)
这似乎已经解决了这个问题,但现在我遇到了一个非常相似的问题,它无法找到嵌套的自定义类型,所以我想活页夹只被调用一次。
System.Runtime.Serialization.SerializationException:无法加载 类型 System.Collections.Generic.List`1[[libcsatquestionnaire.Question, libcsatques...
System.Runtime.Serialization.SerializationException 无法加载 类型 System.Collections.Generic.List`1[[libcsatquestionnaire.Question, libcsatquestionnaire,版本=1.0.0.0,文化=中性, 反序列化需要 PublicKeyToken=null]]。在 System.Runtime.Serialization.ObjectManager.CompleteObject(ObjectHolder 持有人,布尔 bObjectFullyComplete) 在 System.Runtime.Serialization.ObjectManager.DoNewlyRegisteredObjectFixups(ObjectHolder 持有人)在 System.Runtime.Serialization.ObjectManager.RegisterObject(对象 obj, Int64 objectID, SerializationInfo info, Int64 idOfContainingObj, MemberInfo 成员,Int32[] arrayIndex) 在 System.Runtime.Serialization.Formatters.Binary.ObjectReader.RegisterObject(对象 obj, ParseRecord pr, ParseRecord objectPr, Boolean bIsString) at System.Runtime.Serialization.Formatters.Binary.ObjectReader.RegisterObject(对象 obj, ParseRecord pr, ParseRecord objectPr) 在 System.Runtime.Serialization.Formatters.Binary.ObjectReader.ParseObjectEnd(ParseRecord 公关)在 System.Runtime.Serialization.Formatters.Binary.ObjectReader.Parse(ParseRecord 公关)在 System.Runtime.Serialization.Formatters.Binary.__BinaryParser.Run()
在 System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize(HeaderHandler 处理程序,__BinaryParser serParser,布尔 fCheck,布尔 isCrossAppDomain, IMethodCallMessage methodCallMessage) 在 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(流 serializationStream, HeaderHandler 处理程序, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage) 在 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(流 serializationStream、HeaderHandler 处理程序、布尔 fCheck、 IMethodCallMessage methodCallMessage) 在 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(流 序列化流,HeaderHandler 处理程序,布尔 fCheck)在 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(流 序列化流,HeaderHandler 处理程序)在 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(流 序列化流)在 libcsatmigrations_tests.BinaryToYAMLMigratorTests.CanMigrateToYAML() 在 C:\Users\James H\Documents\repos\CSAT-Tooling\libcsatmigrations_tests\BinaryToYAMLMigratorTests.cs:line 21
所以,我的问题是,如何创建一个绑定器来反序列化这些嵌套类型?
【问题讨论】:
-
只是部分开玩笑:我祈祷你的理智。老实说,在您拥有具有正确名称和命名空间(以及强名称/程序集标识)的类型版本之前,可能会更容易恢复,然后反序列化,然后使用 anything else 来存储数据i>,在点燃旧数据和旧类型之前,在它们燃烧的同时跳舞。
-
不幸的是,旧数据分布在大约 100 台工程师笔记本电脑上。我真的不希望他们不得不处理一些 hacky 迁移,我只想给他们一个更新,为他们做这一切......最坏的情况,我会这样做。编写一个将所有内容通过 FTP 传输给我的脚本,我会处理它。关于我可以去哪里的任何想法?
标签: c# serialization binding