2010-09-28 2 views
18

Я получаю следующее исключение при попытке сериализации объекта с помощью XMLSerialization.Циркулярная ссылка при использовании XML-сериализации?

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

Я знаю, что циклическая ссылка, потому что Objecta может иметь childObject из ObjectB и ObjectB-х parentObject является Objecta, однако я хотел бы сохранить эту ссылку, если это возможно. Есть ли способ заставить этот объект сериализоваться с XML-сериализацией без потери каких-либо данных во время процесса сериализации? Я не очень знакомы с сериализацией, поэтому я надеюсь, что у меня будет какой-то атрибут, который я мог бы установить.

ответ

23

Существует несколько вариантов в зависимости от типа сериализатора.

Если бы вы могли использовать DataContractSerializer или BinaryFormatter, то вы можете использовать OnSerializedAttribute и установить родительское свойство для дочернего объекта к этому:

[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; } 
} 
+0

Если я это сделаю, ссылка исчезнет, ​​когда я десериализую объект. Объект исходит из службы WCF – Rachel

+1

Спасибо! Ваши изменения прояснили ... Я полностью забыл, что могу добавить Parent '[OnDeserializing()]', что я и делал. – Rachel

+0

Как я уже упоминал в комментарии ответов AHM, OnDeserializingAttribute избыточен в этом случае, потому что он по-прежнему отлично работает без него. Но вы по-прежнему можете добавить дополнительную логику в этот метод. –

1

Отметьте свойство parentObject как [NonSerialized].

http://blog.kowalczyk.info/article/Serialization-in-C.html

+0

Если я помечен как NonSerialized, тогда данные будут удалены, как только я десериализую объект. – Rachel

+0

NonSerialized не используется XmlSerializer –

2

Если вы можете использовать DataContractSerializer вместо XMLSerializer, то вы можете использовать свойство IsReference на атрибут DataContract. Включение этого будет содержать ссылки, чтобы они были воссозданы при десериализации.

DataContractSerializer также сериализуется в XML, но у вас есть несколько меньше контроля над тем, как выглядит вывод, что вы делаете со старым XMLSerializer. Вы можете узнать больше о сериализаторах здесь: http://www.danrigsby.com/blog/index.php/2008/03/07/xmlserializer-vs-datacontractserializer-serialization-in-wcf/

+0

Спасибо, я рассмотрю это. У вас есть пример того, как я установил бы это значение для класса, как атрибут '[Serializable]' вместо того, чтобы быть определенным как '[DataContract]'? – Rachel

+0

Пытаюсь найти способ сделать это, не переключаясь на атрибуты '[DataContract]'. Мой базовый класс используется для всего, и если я его модифицирую, мне нужно изменить почти все классы в моей ObjectLibrary, чтобы использовать ' [DataContract] 'вместо' [Serializable] 'и добавить атрибут' [DataMember] 'ко всем свойствам и не имеют понятия, как это сделать быстро ... – Rachel

+0

Вы не можете сделать это, не переключаясь на DataContract. Это просто не поддерживается. Если вы хотите использовать атрибут Serializable, вам нужно сделать что-то вроде Сергея, и вызвать код для исправления ссылок после десериализации. – AHM

Смежные вопросы