6

私は次のようなクラスを持っています:

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

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

親を逆シリアル化するとき、各子のそれぞれがその親への参照を持つようにします。問題は、逆シリアル化プロセスのどこで子の「親」ポインタを設定できるかということです。逆シリアル化では常にデフォルトのコンストラクターが使用されるため、子にカスタムコンストラクターを使用できないようです。ISerializable を実装すると、親が作成されるまでに子オブジェクトが既に作成されているように見えます。これを達成する別の方法はありますか?

4

4 に答える 4

10

BinaryFormatterXmlSerializerおよびでは、循環参照の処理が異なり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();
        }

    }
}

を使用する場合は、属性のIsReferenceプロパティをDataContractSerializer使用して、 DataContract をシリアル化および逆シリアル化する際の参照追跡を有効にします。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();
    }

}
于 2012-03-16T19:37:48.430 に答える
3

自動逆シリアル化が機能しない場合は、Parent クラスにIDeserializationCallback インターフェイスを実装させ、 OnDeserialization メソッドで子を更新することができます。

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

  void IDeserializationCallback.OnDeserialization(Object sender) 
  {
    if (null != children)
    {
      children.ForEach(c => c.parent = this);
    }
  }
}
于 2012-03-16T18:52:50.963 に答える
3

そのままにして、Parent を Child クラスの public 読み取り/書き込みプロパティにすると、.NET 自動シリアル化プロセスによって適切に処理されます。

于 2008-12-09T17:28:10.400 に答える
0

子オブジェクトのコレクション クラスの Add メソッドをオーバーライドして、親オブジェクトの一意の識別子を使用して子クラスのプロパティ値を「設定」することで、これを (一種の) 実現しました。

 public class Connections: List<Connection>
    {       public new void Add(Connection connection)
        {
            connection.ApplicationName = ApplicationName;
            base.Add(connection);
        }
    }
于 2008-12-09T17:31:24.450 に答える