2015-11-12 62 views
3

我想实现一个C#类的ISerializable其中包含类似的子类型列表。考虑下面的例子:ISerializable与递归儿童

using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Runtime.Serialization; 
using System.Runtime.Serialization.Formatters.Binary; 

namespace serialisation 
{ 
    [Serializable] 
    internal class Nested : ISerializable 
    { 
     public string Name { get; set; } 

     public List<Nested> Children { get; set; } 

     public Nested(string name) 
     { 
      Name = name; 
      Children = new List<Nested>(); 
     } 

     protected Nested(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) 
     { 
      Name = info.GetString("Name"); 

      // This doesn't work: 
      Nested[] children = (Nested[])info.GetValue("Children", typeof(Nested[])); 
      Children = new List<Nested>(children); 

      // This works: 
      // Children = (List<Nested>)info.GetValue("Children", typeof(List<Nested>)); 
     } 

     public void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) 
     { 
      info.AddValue("Name", Name); 

      // This doesn't work: 
      info.AddValue("Children", Children.ToArray()); 

      // This works: 
      //info.AddValue("Children", Children); 
     } 
    } 

    internal class Program 
    { 
     private static void Main(string[] args) 
     { 
      // Generate a hierarchy 
      Nested root = new Nested("root"); 
      Nested child1 = new Nested("child1"); 
      Nested child2 = new Nested("child2"); 
      Nested child3 = new Nested("child3"); 
      child1.Children.Add(child2); 
      child1.Children.Add(child3); 
      root.Children.Add(child1); 

      Nested deserialized; 
      BinaryFormatter binaryFmt = new BinaryFormatter(); 

      // Serialize 
      using (var fs = new FileStream("Nested.xml", FileMode.OpenOrCreate)) 
      { 
       binaryFmt.Serialize(fs, root); 
      } 

      // Deserialize 
      using (var fs = new FileStream("Nested.xml", FileMode.OpenOrCreate)) 
      { 
       deserialized = (Nested)binaryFmt.Deserialize(fs); 
      } 

      // deserialized.Children contains one null child 
      Console.WriteLine("Original Name: {0}", root.Name); 
      Console.WriteLine("New Name: {0}", deserialized.Name); 
     } 
    } 
} 

在上面的示例,Nested.GetObjectData和嵌套串行构造被调用的4倍,一个接一个。

将子元素作为Nested数组添加到序列化程序将在反序列化时返回正确大小的数组,但所有元素都将为null。

但是,将类型从嵌套数组更改为嵌套列表将会在调用子组件的构造函数后奇迹般地修复null元素。

我想知道的是:

  1. 什么特别的嵌套列表?
  2. 推荐使用递归结构来序列化类的建议方法是什么?

更新:

似乎有额外的接口,IDeserializationCallback.OnDeserialization,这是反序列化后调用已经发生(调用顺序是不确定的)。您可以将反序列化的数组存储在构造函数中的临时成员变量中,然后将其分配给此方法中的列表。除非我错过了一些东西,否则这看起来不太理想,因为你必须用临时变量混乱你的实现。

+0

我相信这是一个已经回答了类似的问题:http://stackoverflow.com/questions/4339602 /阵列反序列化总是给出一个空数组 –

+0

我几分钟前也发现了同样的问题。我之前的搜索条件一定不够具体...... – ChocolatePocket

回答

0

我会走混合模式。下面的解决方案解决了BinaryFormatter(如在您的主要)和XmlSerializer方法,如果您要使用它。 CompositeComponent替换您的Nested类。

[Serializable()] 
[XmlRoot("component", Namespace="", IsNullable=false)] 
public partial class CT_Component 
{ 
    [XmlAttribute("name")] 
    public string Name { get; set;} 
} 

[Serializable()] 
[XmlRoot("composite", Namespace="", IsNullable=false)] 
public partial class CT_Composite 
{ 
    [XmlElement("component", typeof(CT_Component))] 
    [XmlElement("composite", typeof(CT_Composite))] 
    public object[] Items { get; set; } 

    [XmlAttribute("name")] 
    public string Name { get; set; } 
} 

我创建的那些从以下XSD,我总是从XSD到生成的类,因为我永远不能得到属性装饰权利。它的要点是递归CT_Composite类型:

<xs:element name="component" type="CT_Component" /> 
<xs:element name="composite" type="CT_Composite" /> 
<xs:complexType name="CT_Component"> 
    <xs:attribute name="name" type="xs:string" use="required" /> 
</xs:complexType> 
<xs:complexType name="CT_Composite" > 
    <xs:choice minOccurs="1" maxOccurs="unbounded"> 
    <xs:element ref="component" /> 
    <xs:element name="composite" type="CT_Composite" /> 
    </xs:choice> 
    <xs:attribute name="name" type="xs:string" use="required" /> 
</xs:complexType> 

序列化码是相同的。变量的声明:

var composite = new CT_Composite() { 
      Name = "root", 
      Items = new object[] { 
       new CT_Composite() { 
        Name = "child1", 
        Items = new object[] { 
         new CT_Component() {Name="child2"}, 
         new CT_Component() {Name="child3"} 
        } } } }; 

你应该更加正统的,关于模式,你可以使用:

[Serializable()] 
[XmlRoot("component", Namespace="", IsNullable=false)] 
public class Component { 
    [XmlAttribute("name")] public string Name { get; set;} 
} 

[Serializable()] 
[XmlRoot("composite", Namespace="", IsNullable=false)] 
public class Composite : Component { 
    [XmlElement("component", typeof(Component))] 
    [XmlElement("composite", typeof(Composite))] 
    public object[] Items { get; set; } 
}