Create a pointer to the parent during deserialization in C #

I have classes like:

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

When I deserialize Parent, I want each of these children to have a reference to the parent. The question is, where in the deserialization process can I set the child "parent" pointer? I cannot use a special constructor for a child, because deserialization always uses the default constructor. If I implement ISerializable, then it seems that the child objects are already created by the time the parent is created. Is there any other way to achieve this?

+4
source share
4 answers

Loopbacks are handled differently for BinaryFormatter , XmlSerializer and DataContractSerializer .

BinaryFormatter supports BinaryFormatter by default, without work:

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

When using the XmlSerializer avoid looping without serializing the child reference to the parent and make sure the connection is fixed during the deserialization process. This is achieved by implementing the IXmlSerializable interface and handling serialization and deserialization.

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

When using the DataContractSerializer use the IsReference property of the DataContract attribute to enable link tracking when serializing and deserializing 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(); } } 
+8
source

If you leave it alone and let Parent be publicly read / write to the Child class, the .NET automatic serialization process will handle it correctly.

+2
source

If automatic deserialization does not work, you can create the Parent IDeserializationCallback class and update the children in the OnDeserialization Method .

 [Serializable] class Parent : IDeserializationCallback { public List<child> children; void IDeserializationCallback.OnDeserialization(Object sender) { if (null != children) { children.ForEach(c => c.parent = this); } } } 
+2
source

I accomplished this (sort of) by overriding the Add method in the collection class of the child objects to “set” the property value in the child class with a unique identifier for the parent

  public class Connections: List<Connection> { public new void Add(Connection connection) { connection.ApplicationName = ApplicationName; base.Add(connection); } } 
0
source

All Articles