代码之家  ›  专栏  ›  技术社区  ›  Marco

在C的反序列化过程中创建指向父对象的指针#

  •  4
  • Marco  · 技术社区  · 17 年前

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

    当我反序列化父对象时,我希望每个子对象都有对其父对象的引用。问题是,在反序列化过程中,我可以在哪里设置子对象的“父”指针?我似乎不能为子级使用自定义构造函数,因为反序列化总是使用默认构造函数。如果我实现了ISerializable,那么在创建父对象时,子对象似乎已经创建好了。有没有别的方法可以达到这个目的?

    4 回复  |  直到 17 年前
        1
  •  10
  •   Luke Forder    14 年前

    循环引用的处理方式与 BinaryFormatter XmlSerializer DataContractSerializer .

    BinaryFormatter 默认情况下支持循环引用,无需执行任何操作:

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

    XmlSerializer ,避免循环引用,方法是不序列化子级对父级的引用,并确保在反序列化过程中关系是固定的。这是通过实现 IXmlSerializable

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

    当使用 DataContractSerializer ,使用 IsReference DataContract 属性以在序列化和反序列化数据协定时启用引用跟踪。

    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();
        }
    
    }
    
        2
  •  3
  •   Mehrdad Afshari    17 年前

        3
  •  2
  •   Richard Deeming    14 年前

    如果自动反序列化不起作用,可以让父类实现 IDeserializationCallback interface ,并更新 OnDeserialization method

    [Serializable]
    class Parent : IDeserializationCallback 
    {
      public List<child> children;
    
      void IDeserializationCallback.OnDeserialization(Object sender) 
      {
        if (null != children)
        {
          children.ForEach(c => c.parent = this);
        }
      }
    }
    
        4
  •  0
  •   Charles Bretana    17 年前

    我通过重写子对象的集合类中的Add方法,在子类中用父对象的唯一标识符“设置”一个属性值来完成这个(排序)

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