2013-04-03 41 views
3

背景序列化对象消失(BinaryFormatter的)

我有一个对象,我需要为了转移至高性能计算集群用于以后使用

此前序列化,我使用的出我的对象代表一个统计形状模型,并且所有人都愉快地工作了。我的对象变得更加复杂了,我决定通过实现ISerializable定制序列化过程。我继续支持存储在先前格式的数据

的问题

我的问题是一个具体值似乎成功序列化,但始终都是空值,当我试图反序列化。 (没有错误,只是一个非常不愉快的,无用的null)

当我在序列化的时候断开,我可以看到对象被添加到SerializationInfo ok通过检查SerializationInfo并且它有值(它是没有什么奇特的,但会发布下面的代码)

序列化构造函数正在调用(我也放了一个断点),但是当我检查构造函数的SerializationInfo对象时,它没有数据(它没有数据)

UPDATE - download console app here。感谢您寻找

,或者看代码在这里:

守则

类引起的问题:(该PointProfiles属性是有问题的对象)

[Serializable] 
    public class TrainingSet : ITrainingSet, ISerializable 
    { 
     public Dictionary<Tuple<int, int>, IPointTrainingSet> PointProfiles { get; set; } 

     public PrincipalComponentAnalysis PointPCA { get; set; } 

     public double[] AlignedMean { get; set; } 

     public List<Tuple<string, ITransform>> Transforms { get; set; } 

     public string[] FileNames { get; set; } 

     private static Lazy<BinaryFormatter> formatter = new Lazy<BinaryFormatter>(); 

     public static ITrainingSet Load(Guid modelId) 
     { 
      ModelSample s = DataProxy<ModelSample>.AsQueryable().Where(m => m.ModelId == modelId).SingleOrDefault(); 
      if (s == null) 
       return null; 

      byte[] raw = s.Samples.ToArray(); 
      using (MemoryStream ms = new MemoryStream(raw)) 
       return (ITrainingSet)formatter.Value.Deserialize(ms); 

     } 

     void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) 
     { 
      info.AddValue("pca", PointPCA); 

      info.AddValue("tp1", PointProfiles.Select(pp => pp.Key.Item1).ToArray()); 
      info.AddValue("tp2", PointProfiles.Select(pp => pp.Key.Item2).ToArray()); 

      var x = PointProfiles.Select(pp => (ProfileModel)pp.Value).ToArray(); 
      info.AddValue("ipts", x, typeof(ProfileModel[])); 

      info.AddValue("am", AlignedMean); 

      info.AddValue("tname", Transforms.Select(t => t.Item1).ToArray()); 
      info.AddValue("tval", Transforms.Select(t => t.Item2).ToArray()); 
      info.AddValue("fnames", FileNames); 
      info.AddValue("version", 1); // nb 
     } 

     public TrainingSet(SerializationInfo info, StreamingContext context) 
     { 
      int version = 0; 
      foreach(SerializationEntry s in info) 
      { 
       if(s.Name == "version") 
        version = (int)s.Value; 
      } 

      switch(version) 
      { 
       case 0: 
        // old (default binary formatter) 
        PointPCA = info.GetValue("<PointPCA>k__BackingField", typeof(PrincipalComponentAnalysis)) as PrincipalComponentAnalysis; 
        PointProfiles = info.GetValue("<PointProfiles>k__BackingField", typeof(Dictionary<Tuple<int, int>, IPointTrainingSet>)) as Dictionary<Tuple<int, int>, IPointTrainingSet>; 
        AlignedMean = info.GetValue("<AlignedMean>k__BackingField", typeof(double[])) as double[]; 
        Transforms = info.GetValue("<Transforms>k__BackingField", typeof(List<Tuple<string, ITransform>>)) as List<Tuple<string, ITransform>>; 
        FileNames = info.GetValue("<FileNames>k__BackingField", typeof(string[])) as string[]; 

      //stats.PointPCA = pointPCA; 
      //stats.PointProfiles = pointProfiles; 
      //stats.AlignedMean = alignedMean; 
      //stats.Transforms = transforms; 
      //stats.FileNames = fileNames; 

        break; 

       case 1: 
        FileNames = info.GetValue("fnames", typeof(string[])) as string[]; 

        var t = info.GetValue("tval", typeof(ITransform[])) as ITransform[]; 
        var tn = info.GetValue("tname", typeof(string[])) as string[]; 

        Transforms = new List<Tuple<string, ITransform>>(); 
        for(int i = 0;i < tn.Length;i++) 
         Transforms.Add(new Tuple<string,ITransform>(tn[i], t[i])); 

        AlignedMean = info.GetValue("am", typeof(double[])) as double[]; 

        PointPCA = info.GetValue("pca", typeof(PrincipalComponentAnalysis)) as PrincipalComponentAnalysis; 


        var ipts = info.GetValue("ipts", typeof(ProfileModel[])); 

        foreach (var x in info) 
        { 
         int a = 0; 
         a++; // break point here, info has an entry for key "ipts", but it's null (or rather an array of the correct length, and each element of the array is null) 
        } 

        var xxx = ipts as IPointTrainingSet[]; 

        var i2 = info.GetValue("tp2", typeof(int[])) as int[]; 

        var i1 = info.GetValue("tp1", typeof(int[])) as int[]; 

        PointProfiles = new Dictionary<Tuple<int, int>, IPointTrainingSet>(); 
        for (int i = 0; i < i1.Length; i++) 
         PointProfiles.Add(new Tuple<int, int>(i1[i], i2[i]), xxx[i]); 



        break; 

       default: 
        throw new NotImplementedException("TrainingSet version " + version + " is not supported"); 
      } 

     } 

     public TrainingSet() 
     { 

     } 
    } 

Profile类(也可序列化的,这是接下来列出的ProfileModel的基类)

[Serializable] 
    public class Profile : ISerializable, IProfile 
    { 
     public double Angle { get; private set; } 
     public int PointIndex { get; private set; } 
     public int Level { get; set; } 


     public double[,] G { get; private set; } 
     public virtual double[,] GBar { get { throw new InvalidOperationException(); } } 

     public virtual int Width { get { return G.Length; } } 

     public Profile(int level, int pointIndex, double angle, double[,] G) 
     { 
      this.G = G; 
      PointIndex = pointIndex; 
      Level = level; 
      Angle = angle; 
     } 

     // deserialization 
     public Profile(SerializationInfo info, StreamingContext context) 
     { 
      PointIndex = info.GetInt32("p"); 
      Angle = info.GetDouble("a"); 
      G = (double[,])info.GetValue("g", typeof(double[,])); 

      Level = info.GetInt32("l"); 

      //_pca = new Lazy<PrincipalComponentAnalysis>(Pca); 
     } 

     // serialization 
     public void GetObjectData(SerializationInfo info, StreamingContext context) 
     { 
      info.AddValue("p", PointIndex); 
      info.AddValue("a", Angle); 
      info.AddValue("g", G); 
      info.AddValue("l", Level); 
     } 

    } 

和(最后)的ProfileModel类:

[Serializable] 
public class ProfileModel : Profile, ISerializable, IPointTrainingSet 
{ 

    public IProfile MeanProfile { get; private set; } 

    private ProfileModel(int level, int PointIndex, IProfile[] profiles) 
     : base(level, PointIndex, 0, null) 
    { 
     double[,] m = Matrix.Create<double>(profiles.Length, profiles[0].G.Columns(), 0); 

     int idx = 0; 
     foreach (var pg in profiles.Select(p => p.G.GetRow(0))) 
      m.SetRow(idx++, pg); 



     Profile meanProfile = new Profile(level, PointIndex, 0, m.Mean().ToMatrix()); 
     MeanProfile = meanProfile; 
    } 

    // deserialization 
    public ProfileModel(SerializationInfo info, StreamingContext context) : base(info, context) { 
     var ps = info.GetValue("mp", typeof(Profile)); 

     MeanProfile = (IProfile)ps; 
    } 

    // serialization 
    public new void GetObjectData(SerializationInfo info, StreamingContext context) 
    { 
     info.AddValue("mp", MeanProfile, typeof(Profile)); 
     base.GetObjectData(info, context); 
    } 

    public override double[,] GBar 
    { 
     get 
     { 
      return MeanProfile.G; 
     } 
    } 

    public override int Width { get { 
     return GBar.Columns(); 
    } } 
} 

如果你能发现任何东西,我做错了,可能导致这种情况发生,我会非常非常感谢:)

+0

也许它与那个'Tuple '有关? [http://stackoverflow.com/questions/13739348/why-i-could-not-serialize-a-tuple-in-c](http://stackoverflow.com/questions/13739348/why-i-could- not-serialize-a-tuple-in-c) – Rohrbs

+0

@Rohrbs - 感谢您的看法:)我确实在想 - 这就是为什么元组和字典都是以定制的,单独的方式序列化的(请参阅名为“tp1 “和”tp2“ - ”ipts“是字典的第三部分) – Nathan

+0

对不起,在仔细观察后,没有什么会跳出来对我说。也许这里演员'pp =>(int)pp.Key.Item1'?哪些值回到空值? tp1,tp2还是ipts?三个全部? – Rohrbs

回答

2

数组首先进行反序列化,然后再进行内部去极化。当循环浏览ProfileModel的数组时,其内容仍未被分解。

您可以通过实现IDeserializationCallback(或通过将OnDeserilized属性分配给在deserilization完成时应调用的方法)来解决此问题。 OnDeserialzation方法在整个对象图被反序列化之后调用。

您需要藏匿远在私人领域的数组:

private int []i1; 
private int []i2; 
private ProfileModel [] ipts; 

执行下derserialization如下:

ipts = info.GetValue("ipts", typeof(ProfileModel[])); 
i2 = info.GetValue("tp2", typeof(int[])) as int[]; 
i1 = info.GetValue("tp1", typeof(int[])) as int[]; 

并实行IDeserializationCallback:

public void OnDerserilization(object sender) 
{ 
    PointProfiles = new Dictionary<Tuple<int, int>, IPointTrainingSet>(); 

    for (int i = 0; i < i1.Length; i++) 
    PointProfiles.Add(new Tuple<int, int>(i1[i], i2[i]), ipts[i]); 
} 
+0

太棒了,谢谢 - 我会看看那个明天:) – Nathan

+0

那很有用 - 非常感谢! – Nathan

0

我能通过让.NET处理List和Dictionary序列化来代替o来实现这个功能f试图手动完成:

void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) 
{ 
    info.AddValue("pca", PointPCA); 
    info.AddValue("pps", PointProfiles); 
    info.AddValue("am", AlignedMean); 
    info.AddValue("transforms", Transforms); 
    info.AddValue("fnames", FileNames); 
} 

public TrainingSet(SerializationInfo info, StreamingContext context) 
{ 
    PointPCA = info.GetValue("pca", typeof(PrincipalComponentAnalysis)) as PrincipalComponentAnalysis; 
    Transforms = (List<Tuple<string, ITransform>>)info.GetValue("transforms", typeof(List<Tuple<string, ITransform>>)); 
    AlignedMean = info.GetValue("am", typeof(double[])) as double[]; 
    PointProfiles = (Dictionary<Tuple<int, int>, IPointTrainingSet>)info.GetValue("pps", typeof(Dictionary<Tuple<int, int>, IPointTrainingSet>)); 
    FileNames = info.GetValue("fnames", typeof(string[])) as string[]; 
} 

您有一个非常复杂的对象图。我试图打破构造函数ProfileProfileModel,并有一些奇怪的结果,但这个简单的版本似乎工作。

+0

非常感谢您的关注,但我已经拥有了这样的工作原理 - 我自己也是这样做的,因为Mono在处理这些问题时处理得并不好(我将它在Microsoft环境中序列化并且在linux环境中对它进行反序列化)。 – Nathan