我有一个类:
[Serializable]
public class child {
public Parent parent;
}
[Serializable]
public class Parent {
public List<child> children;
}
当我反序列化Parent时,我希望每个孩子都有一个对它的父级的引用。问题是,在反序列化过程中我可以设置孩子的“父”指针吗?我似乎无法为子项使用自定义构造函数,因为反序列化始终使用默认构造函数。如果我实现了ISerializable,那么似乎在创建父级时已经创建了子对象。还有另一种方法可以达到这个目的吗?
答案 0 :(得分:10)
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
时,请使用DataContract
属性的IsReference属性在序列化和反序列化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();
}
}
答案 1 :(得分:2)
如果不管它并让Parent成为Child类的公共读/写属性,.NET自动序列化过程将正确处理它。
答案 2 :(得分:2)
如果自动反序列化不起作用,您可以让您的Parent类实现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);
}
}
}
答案 3 :(得分:0)
我通过覆盖子对象的集合类中的Add方法来完成这个(某种),使用父对象的唯一标识符“设置”子类中的属性值
public class Connections: List<Connection>
{ public new void Add(Connection connection)
{
connection.ApplicationName = ApplicationName;
base.Add(connection);
}
}