【问题标题】:.NET XmlSerializer and multiple references to the same object.NET XmlSerializer 和对同一对象的多个引用
【发布时间】:2010-01-08 08:13:19
【问题描述】:

我的存储库有 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 序列化/反序列化对同一对象的多个引用?

【问题讨论】:

    标签: .net xml xml-serialization object-reference


    【解决方案1】:

    哦,序列化的痛苦 :-> ...

    从来没有通用的解决方案,我想这就是 MS 将其从 Silverlight 框架中剥离出来的原因。

    我从不依赖 .net 框架的任何自动序列化机制。对于我自己的模型和存储库,我通常知道或可以很容易地以编程方式确定哪些属性是简单的标量属性(数字/字符串/等),哪些是指向其他对象的链接(以及哪些是两者的列表)。

    基本上有两种情况:

    1:我们只想序列化/传输对象的平面信息。在这种情况下,我只传输链接到其他对象的属性的相应 ID。然后接收者可以进行后续查询以获取他们需要的所有其他对象。

    2:我们希望传输尽可能多的信息,即具有多个级别的更深层次的嵌套 XML,主要用于某些报告功能,仅使用 XML 上的一些 CSS 直接显示所有内容。在这种情况下,实际上希望将相同的对象多次解析到 XML 树中。

    有时我需要稍微调整第一个场景以避免过多的后续查询调用,但通常我相处得很好。 IE。我已经在我们的代码库中构建了我们可以指定我们想要在什么时候解析哪些附加对象,和/或它在某处配置。

    【讨论】:

    • 非常令人放心 - 作为新手,我认为我一定错过了一些东西。由于我的问题与您的第一个场景有关,我将按照您的建议在代码库中构建一些东西来整理反序列化。也许我会创建一个 IXmlFinalizeDeserialization,它可以在我的所有对象中调用,跟踪那些反序列化不充分的对象并修复引用。
    【解决方案2】:

    没有使用 XML 序列化程序解决此问题的方法。它没有可以用来消除重复的身份概念。

    您能做的最好的事情就是将对象池与其引用分开序列化。然后,您可以在反序列化后重新创建列表。

    顺便说一句,您是否知道 XmlSerializer 并非特定于 C#?

    【讨论】:

    • 谢谢约翰 - 非常简洁。我已经从我的标签中删除了 c#。
    【解决方案3】:

    您可以实现接口 IXmlSerializable 到 Enrollment 并在 WriteXml 方法中生成仅包含键的学生和课程 XML,例如:

    <Student Id="5"/>
    <Course Id="6"/>
    

    在 ReadXml 方法中,您可以从中加载引用。您还必须将 XmlIgnore 属性设置为 Student 和 Course 属性。

    【讨论】:

    • 这种方法的问题在于,在 ReadXml 期间,您无法将引用 (AFAIK) 加载到也被反序列化的其他数据,因为在反序列化完成之前它不可用。你知道这个吗?
    【解决方案4】:

    这听起来如何是一种解决方案:

    1. XML忽略每个辅助 参考即 Enrolment.Student & 注册.课程
    2. 为每个辅助创建一个属性 用于参考 序列化/反序列化外键 代替该参考 - 前缀 与 XML_FK。例如 XML_FK_Student & XML_FK_Course
    3. 创建一个方法 XML_FinalizeDeserialization 是 反序列化加载后调用 使用那些外国的参考文献 关键属性。

    【讨论】:

    • 如果我们可以创建一个属性 [XmlSecondary] 我们放在一个引用之前,这会导致 XML_Serializer 检查以 [XMLPK] 为前缀的字段的引用并只输出那些引用,这不是很好吗?可能吗?
    • 很有可能,但是您需要编写自己的序列化程序来识别该属性,我认为您不想这样做。
    【解决方案5】:

    您应该/可以将引用跟踪与 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)]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-05-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-28
      相关资源
      最近更新 更多