.NET XmlSerializer 和对同一对象的多个引用
-
19-09-2019 - |
题
我的存储库有 List<Student>
, List<Course>
和 List<Enrolment>
其中 Enrollment 具有 Enrolment.Student 和 Enrolment.Course,它们引用前两个列表中的学生或课程之一。
当我在存储库上使用 XmlSerializer 时,它会输出冗余数据,因为它会序列化每个学生的所有属性 List<Student>
然后再一次,每次提到这些相同的学生 List<Enrolment>
. 。我正在寻找一种优雅的方法来解决这个问题。
反序列化后,我可以使用反序列化创建的重复对象实例中的 ID 值来修复引用,但这看起来很黑客。
修复冗余输出的一种方法是 XmlIgnore Enrolment.Student 和 Enrolment.Course,并创建另外两个属性用于序列化 - Enrolment.StudentID 和 Enrolment.CourseID。但是,在反序列化过程中,无法设置 Enrolment.Student 和 Enrolment.Course 的引用(AFAIK),因为反序列化的结果 List<Student>
和 List<Course>
不可用。
我想到的另一种方法是序列化对象层次结构中的较低位置,分别执行每个列表并控制反序列化的顺序 - 我宁愿不这样做。
另一种方法是 XmlIgnore List<Enrolment>
并创建一个注册序列化帮助程序类来初始化 List<Enrolment>
自身反序列化完成后。这看起来需要付出很大的努力。
其他人如何使用 XmlSerializer 序列化/反序列化对同一对象的多个引用?
解决方案
喔系列化的痛苦: - > ...
有从来都不是一个通用的解决方案,我想这就是为什么MS剥离出来Silverlight框架的。
我从不依赖于.NET框架的任何自动序列化机制。对于我自己的模型和资料库,我通常都知道,也可以很容易地编程方式确定哪些属性是简单的标者(数字/字符串/等),它们是链接到其他对象(以及它们要么列表)。
有基本上2种方案:
1:我们想要序列/传送仅物体的平信息。在这种情况下我仅传送各个ID为链接到其它对象的属性。然后,接收器可以使随后的查询,以获得他们所需要的所有其他对象。
2:我们希望尽可能多的信息可能,即更深嵌套的XML与几个层次转移,多为直接使用仅仅在XML一些CSS一些报表功能显示的一切。在这种情况下,它实际上是希望是相同的对象将多次被解析成XML树。
有时候,我需要调整的第一个场景一点点,以避免过多的后续查询电话,但通常我相处得很好。即我已经内置到我们的代码库,我们可以指定希望解决时附加的对象,和/或它的某个地方进行配置。
其他提示
存在使用XML序列针对此问题没有解决方案。它没有身份的一个概念,它可能会使用删除重复。
你能做的最好的是从他们引用单独序列化对象池。然后,您可以反序列化后重新创建列表。
顺便说一句,你知道,XmlSerializer的是不特定于C#?
您可以实现接口IXmlSerializable的招生和在方法中WriteXML产生学生,当然XML这将只包含键e.g:
<Student Id="5"/>
<Course Id="6"/>
和在ReadXml方法可以加载来自该引用。您还必须设置XmlIgnore属性为学生和课程属性。
这听起来如何作为一个解决方案:
- xmlignore每个二次参考IE入学。
- 为每个辅助引用创建一个属性,该属性用于序列化/供应for for该参考的外键 - 带有XML_FK的前缀。例如xml_fk_student&xml_fk_course
- 创建一个方法xml_finalizedeserialization,该方法在避难所化后使用这些外键属性加载引用。
您应该/可以使用参考跟踪与datacontract串行器:
//deserilaize:
using(MemoryStream memStmBack = new MemoryStream()) {
var serializerForth = new DataContractSerializer(
typeof(YourType),
null,
0x7FFF /*maxItemsInObjectGraph*/ ,
false /*ignoreExtensionDataObject*/ ,
true /*preserveObjectReferences*/ ,
null /*dataContractSurrogate*/ );
byte[] data = System.Text.Encoding.UTF8.GetBytes(xml);
memStmBack.Write(data, 0, data.Length);
memStmBack.Position = 0;
var lsBack = (YourType) serializerForth.ReadObject(memStmBack);
}
//serialize...
using(MemoryStream memStm = new MemoryStream()) {
var serializer = new DataContractSerializer(
typeof(YourType),
knownTypes,
0x7FFF /*maxItemsInObjectGraph*/ ,
false /*ignoreExtensionDataObject*/ ,
true /*preserveObjectReferences*/ ,
null /*dataContractSurrogate*/ );
serializer.WriteObject(memStm, yourType);
memStm.Seek(0, SeekOrigin.Begin);
using(var streamReader = new StreamReader(memStm)) {
result = streamReader.ReadToEnd();
或者使用
[Serializable]
[DataContract(IsReference = true)]