Soru XML Serileştirme kullanırken Dairesel Referans?


XMLSerialization kullanarak bir nesneyi serileştirmeye çalışırken aşağıdaki özel durumu alıyorum.

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

Döngüsel referansın, ObjectA’nın childObject ObjectB ve ObjectB'ler parentObject ObjectA ise, mümkünse bu referansı saklamak istiyorum. Bu nesneyi serileştirme işlemi sırasında herhangi bir veri kaybetmeden XML Serileştirme ile seri hale getirmenin bir yolu var mı? Ben serileştirme ile çok familar değilim, bu yüzden ayarlayabileceğim bir çeşit Attribute umuyordum.


18
2017-09-28 16:59


Menşei




Cevaplar:


Seri hale getirici tipine bağlı olarak birkaç seçenek vardır.

Eğer kullanabilseydin DataContractSerializer veya BinaryFormatter o zaman kullanabilirsiniz OnSerializedAttribute ve çocuğunuzun nesnesi için Ebeveyn özelliğini şöyle ayarlayın:

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

}

Kullanmak isterseniz XmlSerializer uygulamalısın IXmlSerializable, kullan XmlIgnoreAttribute ve ReadXml yönteminde aşağı yukarı aynı mantığı uyguladı. Ancak bu durumda, tüm Xml serileştirme mantığını manuel olarak da uygulamanız gerekir:

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

23
2017-09-28 17:43



Bunu yaparsam nesneyi kaldırdığımda referans geçer. Nesne bir WCF hizmetinden geliyor - Rachel
Teşekkür ederim! Düzenlemenizin açıkladığı şeyler ... Ebeveyni ekleyebileceğimi tamamen unuttum [OnDeserializing()]Yaptığım şey budur. - Rachel
AHM'nin cevapları yorumunda belirttiğim gibi, OnDeserializingAttribute bu durumda gereksizdir, çünkü hala onsuz iyi çalışıyor. Ancak yine de bu yöntemde ek bir mantık ekleyebilirsiniz. - Sergey Teplyakov


XMLSerializer yerine DataContractSerializer'ı kullanabiliyorsanız DataContract özniteliğinde IsReference özelliğini kullanabilirsiniz. Bunu etkinleştirmek referansları koruyacak, böylece serileştirme üzerine yeniden oluşturulacaklar.

DataContractSerializer da XML'e serileştirir, ancak çıktının neye benzediğini biraz daha az kontrol edersiniz, eski XMLSerializer ile yaptığınız gibi. Burada serileştiriciler hakkında daha fazla bilgi edinebilirsiniz: http://www.danrigsby.com/blog/index.php/2008/03/07/xmlserializer-vs-datacontractserializer-serialization-in-wcf/


2
2017-09-28 17:14



Teşekkür ederim, buna bakacağım. Bu değeri bir sınıfta nasıl ayarlayacağına dair bir örnek var mı? [Serializable] olarak tanımlanmak yerine [DataContract]? - Rachel
Geçmeden bunu yapmanın bir yolunu bulmaya çalışıyorum. [DataContract] öznitelikler ... Temel sınıfım her şey için kullanılır ve bunu değiştirirsem, ObjectLibrary'mdeki tüm sınıfları değiştirmek zorundayım. [DataContract] yerine [Serializable] ve ekle [DataMember] tüm özelliklere atıfta bulun ve bu kadar hızlı nasıl yapılacağına dair bir fikriniz yok ... - Rachel
DataContract'a geçmeden bunu yapamazsınız. Sadece desteklenmiyor. Eğer Serializable özniteliğini kullanmak istiyorsanız, o zaman Sergey'in önerdiği gibi bir şey yapmalısınız ve desantizasyondan sonra referansları düzeltmek için bazı kodları çağırmalısınız. - AHM
Teşekkür ederim. DataContract / DataMember özniteliklerini kullanmak için tüm sınıflarımı yeniden yazmak istemediğimden, ancak çözüm bulmak için çok fazla sınıfım olmasaydı, iyi bir çözüm olacağından dolayı, +1'in herşeyi yeniden yazmak istemediğim için, Sergey'in çözümü ile sona erdi. - Rachel
Aslında BinaryFormatter ve DataContractSerializer'ın yardım almadan döngüsel referanslarla başa çıkmasını özledim. Yani OnSerializedAttribute ile tüm bu mantık reduntant. - Sergey Teplyakov


ParentObject özelliğini [NonSerialized] olarak işaretleyin.

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


1
2017-09-28 17:08



Eğer NonSerialized'i işaretlerseniz, o zaman nesneyi seri hale getirdiğimde veri geçer. - Rachel
NonSerialized XmlSerializer tarafından kullanılmaz - Marc Gravell♦