2010-09-28 23 views
18

Otrzymuję następujący wyjątek podczas próby serializacji obiektu przy użyciu XMLSerialization.Cykliczne odniesienie podczas korzystania z szeregowania XML?

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

wiem okrągły odniesienia ponieważ ObjectA może mieć childObject z objectB i objectB na parentObject jest ObjectA jednak chciałbym zachować tę nazwę, jeśli to możliwe. Czy istnieje sposób, aby ten obiekt serializować z serią XML bez utraty danych podczas procesu serializacji? Nie jestem bardzo znany z serializacji, więc mam nadzieję, że istnieje jakiś atrybut, który mógłbym ustawić.

Odpowiedz

23

Istnieje kilka opcji w zależności od typu serializera.

Jeśli można użyć DataContractSerializer lub BinaryFormatter następnie można użyć OnSerializedAttribute i ustaw właściwość Parent do obiektu podrzędnego do tego:

[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); 
     } 
    } 

} 

Jeśli chcesz użyć XmlSerializer należy wdrożyć IXmlSerializable użyć XmlIgnoreAttribute i wdrożone więcej lub mniej ta sama logika w metodzie ReadXml. Ale w tym przypadku należy również ręcznie zaimplementować całą logikę serializacji 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

Jeśli to zrobię, odniesienie zniknie, gdy deserializuję obiekt. Obiekt pochodzi z usługi WCF. – Rachel

+1

Dziękujemy! Twoja edycja wyjaśniła rzeczy ... Całkowicie zapomniałem, że mogę dodać Parent "[OnDeserializing()]", co właśnie zrobiłem. – Rachel

+0

Jak wspomniałem z komentarzem odpowiedzi AHM, OnDeserializingAttribute jest w tym przypadku nadmiarowym, ponieważ nadal działa bez niego. Ale wciąż możesz dodać dodatkową logikę w tej metodzie. –

2

Jeśli można użyć DataContractSerializer zamiast XMLSerializer następnie można użyć właściwości IsReference na atrybutem DataContract. Włączenie tej opcji spowoduje zachowanie referencji, dzięki czemu zostaną one odtworzone po deserializacji.

DataContractSerializer również serializuje do XML, ale masz nieco mniej kontroli nad tym, jak wygląda wynik, co robisz ze starszym XMLSerializer. Więcej informacji o serializerach można znaleźć tutaj: http://www.danrigsby.com/blog/index.php/2008/03/07/xmlserializer-vs-datacontractserializer-serialization-in-wcf/

+0

Dziękuję, zagłębię się w to. Czy masz przykład, w jaki sposób ustawić tę wartość na klasy, tak samo jak atrybut [Serializable] ', zamiast być zdefiniowany jako' [DataContract] '? – Rachel

+0

Ugh próbuje znaleźć sposób, aby to zrobić bez przełączania się do atrybutów "[DataContract]" .... Moja klasa bazowa jest używana do wszystkiego i jeśli to zmienię, muszę zmodyfikować prawie wszystkie klasy w mojej ObjectLibrary, aby użyć ' [DataContract] 'zamiast' [Serializable] 'i dodaj atrybut' [DataMember] 'do wszystkich właściwości i nie masz pojęcia, jak to zrobić szybko ... – Rachel

+0

Nie możesz tego zrobić bez przełączania się do DataContract. To nie jest obsługiwane. Jeśli chcesz użyć atrybutu Serializable, musisz zrobić coś takiego, jak sugeruje Sergey, i wywołać jakiś kod, aby naprawić referencje po deserializacji. – AHM

Powiązane problemy