Utworzyć wskaźnik do obiektu nadrzędnego w procesie deserializacji w C #

głosy
4

Mam zajęcia takie jak:

[Serializable]
public class child {
     public Parent parent;
}

[Serializable]
public class Parent {
  public List<child> children;
}

Kiedy deserializowania Parent, chcę każdy z poszczególnych dzieci mają odniesienie do jego rodzicem. Chodzi o to, gdzie w procesie deserializacji mogę ustawić „rodzic” dziecka wyżeł? I nie wydaje się, aby użyć konstruktora niestandardową dla dziecka, ponieważ deserializacji zawsze używa domyślnego konstruktora. Jeśli zaimplementować ISerializable, to wydaje się, że obiekty podrzędne zostały już utworzone przez czas rodzic jest tworzony. Czy istnieje inny sposób osiągnięcia tego celu?

Utwórz 09/12/2008 o 18:20
źródło użytkownik
W innych językach...                            


4 odpowiedzi

głosy
8

Okrągłe odniesienia są traktowane odmiennie dla BinaryFormatter, XmlSerializeri DataContractSerializer.

Te BinaryFormatterpodpory okrągłe referencje domyślnie, nie wymaga prac:

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

[Serializable]
public class Child
{
    public Guid Id { get; set; }

    public Parent parent;
}

[Serializable]
public class Parent
{
    public Guid Id;

    public List<Child> Children;
}

class Program
{
    static void Main(string[] args)
    {
        Child c1 = new Child { Id = Guid.NewGuid() };
        Child c2 = new Child { Id = Guid.NewGuid() };

        Parent p = new Parent { Id = Guid.NewGuid(), Children = new List<Child> { c1, c2 } };

        c1.parent = p;
        c2.parent = p;

        using (var stream1 = new MemoryStream())
        {
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.Serialize(stream1, p);
            stream1.Position = 0;

            var deserializedParent = formatter.Deserialize(stream1) as Parent;
            foreach (var child in deserializedParent.Children)
            {
                Console.WriteLine("Child Id: {0}, Parent Id: {1}", child.Id, child.parent.Id);
            }
        }

        Console.ReadLine();
    }
}

Po użyciu XmlSerializer, unikać okrągłą refernce przez nie szeregowania odesłanie dziecka do rodzica i upewnić się, że relacja jest ustalona w procesie deserializacji. Odbywa się to poprzez wdrożenie IXmlSerializableinterfejsu i obsługi serializacji i deserializacji.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;

namespace XmlSerialization
{

    [Serializable]
    public class Child
    {
        public Guid Id { get; set; }

        [XmlIgnore] // Don't serialize the reference to the parent
        public Parent parent;
    }

    [Serializable]
    public class Parent : IXmlSerializable
    {
        public List<Child> Children;

        public Guid Id;

        public System.Xml.Schema.XmlSchema GetSchema()
        {
            return null;
        }

        public void ReadXml(System.Xml.XmlReader reader)
        {
            XElement xml = XElement.ReadFrom(reader) as XElement;
            if (xml != null)
            {
                // Deserialize Children
                Children = 
                    xml.Descendants("Child")
                       .Select(x => new Child() { Id = Guid.Parse(x.Element("Id").Value), parent = this })
                       .ToList();

                // Deserialize Id
                Id = Guid.Parse(xml.Attribute("Id").Value); 
            }
        }

        public void WriteXml(System.Xml.XmlWriter writer)
        {
            // Serialize Id
            writer.WriteAttributeString("Id", Id.ToString());

            // Serialize Children
            XmlSerializer childSerializer = new XmlSerializer(typeof(Child));
            foreach (Child child in Children)
            {
                childSerializer.Serialize(writer, child);
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Child c1 = new Child { Id = Guid.NewGuid() };
            Child c2 = new Child { Id = Guid.NewGuid() };

            Parent p = new Parent { Id = Guid.NewGuid(), Children = new List<Child> { c1, c2 } };

            c1.parent = p;
            c2.parent = p;

            using (var stream1 = new MemoryStream())
            {
                XmlSerializer formatter = new XmlSerializer(typeof(Parent), new Type[] { typeof(Child) }) ;
                formatter.Serialize(stream1, p);
                stream1.Position = 0;

                stream1.Position = 0;

                var deserializedParent = formatter.Deserialize(stream1) as Parent;
                foreach (var child in deserializedParent.Children)
                {
                    Console.WriteLine(string.Format("Child Id: {0}, Parent Id: {1}", child.Id,  child.parent.Id ));
                }
            }

            Console.ReadLine();
        }

    }
}

Po użyciu DataContractSerializer, należy użyć IsReference własność DataContractatrybutu, aby umożliwić śledzenie odniesienia podczas szeregowania i deserializacji DataContracts.

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

[DataContract(IsReference = true)]
public class Child
{
    [DataMember]
    public Guid Id { get; set; }

    [DataMember]
    public Parent parent;
}

[DataContract(IsReference = true)]
public class Parent
{
    [DataMember]
    public Guid Id;

    [DataMember]
    public List<Child> Children;
}

class Program
{
    static void Main(string[] args)
    {
        Child c1 = new Child { Id = Guid.NewGuid() };
        Child c2 = new Child { Id = Guid.NewGuid() };

        Parent p = new Parent { Id = Guid.NewGuid(), Children = new List<Child> { c1, c2 } };

        c1.parent = p;
        c2.parent = p;

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

            var deserializedParent = formatter.ReadObject(stream1) as Parent;
            foreach (var child in deserializedParent.Children)
            {
                Console.WriteLine("Child Id: {0}, Parent Id: {1}", child.Id, child.parent.Id);
            }
        }

        Console.ReadLine();
    }

}
Odpowiedział 16/03/2012 o 20:37
źródło użytkownik

głosy
2

Jeśli automatyczne deserializacji nie działa, można mieć klasa nadrzędna zaimplementować interfejs IDeserializationCallback i aktualizować dzieci w metodzie OnDeserialization .

[Serializable]
class Parent : IDeserializationCallback 
{
  public List<child> children;

  void IDeserializationCallback.OnDeserialization(Object sender) 
  {
    if (null != children)
    {
      children.ForEach(c => c.parent = this);
    }
  }
}
Odpowiedział 16/03/2012 o 19:52
źródło użytkownik

głosy
2

Jeśli zostawisz go w spokoju i pozwolić nadrzędna być publiczną właściwość odczytu / zapisu z klasy dziecka, automatyczny proces serializacji .NET będzie go obsługiwać prawidłowo.

Odpowiedział 09/12/2008 o 18:28
źródło użytkownik

głosy
0

I dokonał tego (rodzaj) poprzez nadpisanie metody Add w klasie odbioru obiektu dziecka, aby „ustawić” wartości nieruchomości w klasie dzieci z Unikalny identyfikator obiektu nadrzędnego

 public class Connections: List<Connection>
    {       public new void Add(Connection connection)
        {
            connection.ApplicationName = ApplicationName;
            base.Add(connection);
        }
    }
Odpowiedział 09/12/2008 o 18:31
źródło użytkownik

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more