Crear puntero al objeto principal en el proceso de deserialización en C #

votos
4

Tengo una clase como:

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

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

Cuando deserializo a Parent, quiero que cada uno de los niños tenga una referencia a sus padres. La pregunta es, ¿en qué parte del proceso de deserialización puedo establecer el puntero principal del niño? Parece que no puedo usar un constructor personalizado para el elemento secundario, porque la deserialización siempre usa el constructor predeterminado. Si implemento ISerializable, parece que los objetos secundarios ya se han creado para cuando se creó el padre. ¿Hay alguna otra manera de lograr esto?

Publicado el 09/12/2008 a las 18:20
fuente por usuario
En otros idiomas...                            


4 respuestas

votos
8

Las referencias circulares se manejan de forma diferente para el BinaryFormatter, XmlSerializery DataContractSerializer.

Los BinaryFormattersoportes referencias circulares de forma predeterminada, no hay trabajo requiere:

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

Cuando se utiliza el XmlSerializer, evitar la refernce circular por no serialización de referencia del niño al padre y asegúrese de que la relación es fija durante el proceso de deserialización. Esto se hace mediante la implementación de la IXmlSerializableinterfaz y el manejo de la serialización y deserialización.

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

    }
}

Cuando se utiliza el DataContractSerializer, utilice el IsReference propiedad del DataContractatributo para permitir el seguimiento de referencia cuando serializar y deserializar 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();
    }

}
Respondida el 16/03/2012 a las 20:37
fuente por usuario

votos
2

Si la deserialización automática no funciona, usted podría tener su clase de Padres implementar la interfaz IDeserializationCallback , y actualizar los niños en el método OnDeserialization .

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

  void IDeserializationCallback.OnDeserialization(Object sender) 
  {
    if (null != children)
    {
      children.ForEach(c => c.parent = this);
    }
  }
}
Respondida el 16/03/2012 a las 19:52
fuente por usuario

votos
2

Si lo deja en paz y deja que Parent sea una propiedad pública de lectura / escritura de la clase Child, el proceso de serialización automática de .NET lo manejará correctamente.

Respondida el 09/12/2008 a las 18:28
fuente por usuario

votos
0

Logré esto (más o menos) anulando el método Agregar en la clase de colección del objeto hijo, para 'establecer' un valor de propiedad en la clase hija con el identificador único del objeto padre

 public class Connections: List<Connection>
    {       public new void Add(Connection connection)
        {
            connection.ApplicationName = ApplicationName;
            base.Add(connection);
        }
    }
Respondida el 09/12/2008 a las 18:31
fuente por usuario

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