【问题标题】:Circular Reference when using XML Serialization?使用 XML 序列化时的循环引用?
【发布时间】:2010-09-28 16:59:00
【问题描述】:

我在尝试使用 XMLSerialization 序列化对象时收到以下异常。

A circular reference was detected while serializing an object of type MyObject}

我知道循环引用是因为 ObjectA 可以有 ObjectB 的 childObject,而 ObjectB 的 parentObject 是 ObjectA,但是如果可能,我想保留该引用。有没有办法让这个对象通过 XML 序列化进行序列化,而不会在序列化过程中丢失任何数据?我对序列化不是很熟悉,所以我希望我可以设置某种属性。

【问题讨论】:

  • 在我看来,我觉得还是做可以序列化的新模型比较好

标签: c# serialization


【解决方案1】:

有几个选项取决于序列化程序类型。

如果您可以使用DataContractSerializerBinaryFormatter,那么您可以使用OnSerializedAttribute 并将您的子对象的 Parent 属性设置为:

[Serializable]
public class Child
{
    public string Foo { get; set; }

    public Parent Parent { get { return parent; } set { parent = value; } }

    // We don't want to serialize this property explicitly.
    // But we could set it during parent deserialization
    [NonSerialized]
    private Parent parent;
}

[Serializable]
public class Parent
{
    // BinaryFormatter or DataContractSerializer whould call this method
    // during deserialization
    [OnDeserialized()]
    internal void OnSerializedMethod(StreamingContext context)
    {
        // Setting this as parent property for Child object
        Child.Parent = this;
    }

    public string Boo { get; set; }

    public Child Child { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        Child c = new Child { Foo = "Foo" };
        Parent p = new Parent { Boo = "Boo", Child = c };

        using (var stream1 = new MemoryStream())
        {
            DataContractSerializer serializer = new DataContractSerializer(typeof (Parent));
            serializer.WriteObject(stream1, p);
            stream1.Position = 0;
            var p2 = (Parent)serializer.ReadObject(stream1);

            Console.WriteLine(object.ReferenceEquals(p, p2)); //return false
            Console.WriteLine(p2.Boo); //Prints "Boo"

            //Prints: Is Parent not null: True
            Console.WriteLine("Is Parent not null: {0}", p2.Child.Parent != null);
        }
    }

}

如果你想使用XmlSerializer,你应该实现IXmlSerializable,使用XmlIgnoreAttribute并在ReadXml方法中实现或多或少相同的逻辑。但在这种情况下,您还应该手动实现所有 Xml 序列化逻辑:

[Serializable]
public class Child
{
    public Child()
    {
    }

    public string Foo { get; set; }

    [XmlIgnore]
    public Parent Parent { get; set; }
}

[Serializable]
public class Parent
{
    public Parent()
    {
    }

    #region IXmlSerializable Members

    public System.Xml.Schema.XmlSchema GetSchema()
    {
        throw new NotImplementedException();
    }

    public void ReadXml(System.Xml.XmlReader reader)
    {
        //Reading Parent content
        //Reading Child
        Child.Parent = this;
    }

    public void WriteXml(System.Xml.XmlWriter writer)
    {
        //Writing Parent and Child content
    }

    #endregion

    public string Boo { get; set; }

    public Child Child { get; set; }
}

【讨论】:

  • 如果我这样做了,那么当我反序列化对象时引用就消失了。对象来自 WCF 服务
  • 谢谢!您的编辑澄清了一些事情......我完全忘记了我可以添加父[OnDeserializing()],这就是我最终要做的。
  • 正如我在 AHM 的回答评论中提到的,在这种情况下 OnDeserializingAttribute 是多余的,因为没有它它仍然可以正常工作。但是你仍然可以在这个方法中添加一些额外的逻辑。
【解决方案2】:

如果您可以使用 DataContractSerializer 而不是 XMLSerializer,那么您可以在 DataContract 属性上使用 IsReference 属性。启用此功能将保留引用,以便在反序列化时重新创建它们。

DataContractSerializer 也可以序列化为 XML,但您对输出的控制权比使用旧的 XMLSerializer 时要少一些。您可以在此处阅读有关序列化程序的更多信息:http://www.danrigsby.com/blog/index.php/2008/03/07/xmlserializer-vs-datacontractserializer-serialization-in-wcf/

【讨论】:

  • 谢谢,我会调查的。你有一个例子说明我如何在一个类上设置这个值,就像一个 [Serializable] 属性而不是被定义为一个 [DataContract]
  • 呃试图找到一种方法来做到这一点而不切换到[DataContract] 属性......我的基类用于所有内容,如果我修改它,我必须修改几乎所有的类我的 ObjectLibrary 使用 [DataContract] 而不是 [Serializable] 并将 [DataMember] 属性添加到所有属性并且不知道如何快速做到这一点......
  • 如果不切换到 DataContract,您将无法执行此操作。只是不支持。如果你想使用 Serializable 属性,那么你必须像 Sergey 建议的那样做一些事情,并在反序列化后调用一些代码来修复引用。
  • 谢谢。我最终选择了 Sergey 的解决方案,因为我不想重写我的所有类以使用 DataContract/DataMember 属性,但无论如何 +1 因为如果我没有那么多类要修改,这将是一个很好的解决方案。
  • 实际上我错过了 BinaryFormatter 和 DataContractSerializer 在没有帮助的情况下处理循环引用。所以所有这些 OnSerializedAttribute 的逻辑都是多余的。
【解决方案3】:

将 parentObject 属性标记为 [NonSerialized]。

https://blog.kowalczyk.info/article/8n/serialization-in-c.html

【讨论】:

  • 如果我将其标记为 NonSerialized,那么一旦我反序列化对象,数据就会消失。
  • XmlSerializer 不使用 NonSerialized
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-21
  • 1970-01-01
  • 2021-05-23
  • 2015-02-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多