Gli oggetti serializzati scompaiono (BinaryFormatter)

sfondo

Ho un object che ho bisogno di serializzare per poter passare ad un cluster di elaborazione ad alte prestazioni per un uso successivo

In precedenza, ho utilizzato il formattatore binario out-of-the-box per il mio object che rappresenta un modello statistico di forma e tutto ha funzionato felicemente

Il mio object è diventato più complesso e ho deciso di personalizzare il processo di serializzazione implementando ISerializable. Continuo a supportare i dati memorizzati nel formato precedente

Il problema

Il mio problema è che un valore particolare sembra serializzare correttamente, ma ha sempre un valore null quando tento di deserializzare. (nessun errore, solo un null molto sgradevole, non utile)

Quando interrompo al punto di serializzazione, posso vedere che l’object viene aggiunto a SerializationInfo ok controllando SerializationInfo e che ha dei valori (non è niente di particolare, ma pubblicherà il codice per questo sotto)

Il costruttore di serializzazione viene chiamato (ho messo un break-point anche lì), ma quando ispeziono l’object SerializationInfo del costruttore, non ha dati (ha una voce, solo nessun dato)

AGGIORNA – scarica l’app della console qui . Grazie per aver guardato

oppure, guarda il codice qui:

Il codice

Classe che causa problemi: (la proprietà PointProfiles è l’object incriminato)

[Serializable] public class TrainingSet : ITrainingSet, ISerializable { public Dictionary<Tuple, IPointTrainingSet> PointProfiles { get; set; } public PrincipalComponentAnalysis PointPCA { get; set; } public double[] AlignedMean { get; set; } public List<Tuple> Transforms { get; set; } public string[] FileNames { get; set; } private static Lazy formatter = new Lazy(); public static ITrainingSet Load(Guid modelId) { ModelSample s = DataProxy.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("k__BackingField", typeof(PrincipalComponentAnalysis)) as PrincipalComponentAnalysis; PointProfiles = info.GetValue("k__BackingField", typeof(Dictionary<Tuple, IPointTrainingSet>)) as Dictionary<Tuple, IPointTrainingSet>; AlignedMean = info.GetValue("k__BackingField", typeof(double[])) as double[]; Transforms = info.GetValue("k__BackingField", typeof(List<Tuple>)) as List<Tuple>; FileNames = info.GetValue("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>(); for(int i = 0;i < tn.Length;i++) Transforms.Add(new Tuple(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, IPointTrainingSet>(); for (int i = 0; i < i1.Length; i++) PointProfiles.Add(new Tuple(i1[i], i2[i]), xxx[i]); break; default: throw new NotImplementedException("TrainingSet version " + version + " is not supported"); } } public TrainingSet() { } } 

Classe profilo (anche serializzabile, questa è la class base per ProfileModel che è elencata di seguito)

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

e (Infine) la class 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(profiles.Length, profiles[0].G.Columns(), 0); int idx = 0; foreach (var pg in profiles.Select(p => pGGetRow(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(); } } } 

Se riesci a individuare qualcosa che sto sbagliando che potrebbe causare ciò, sarei molto grato 🙂

L’array deserializza prima e le deserilazazioni interne vengono eseguite in seguito. Quando si passa attraverso l’array di ProfileModel, il suo contenuto non è stato ancora derserializzato.

Probabilmente si può risolvere questo problema implementando IDeserializationCallback (o assegnando l’attributo OnDeserilized a un metodo che dovrebbe essere chiamato al completamento della deserilizzazione). Il metodo OnDeserialzation viene chiamato dopo che l’intero grafico dell’object è deserializzato.

Dovresti riporre i tuoi array in campi privati:

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

Effettuare quanto segue alla derserializzazione:

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

E implementare IDeserializationCallback:

 public void OnDerserilization(object sender) { PointProfiles = new Dictionary, IPointTrainingSet>(); for (int i = 0; i < i1.Length; i++) PointProfiles.Add(new Tuple(i1[i], i2[i]), ipts[i]); } 

Sono stato in grado di farlo funzionare lasciando che .NET gestisse la serializzazione di elenchi e dizionari invece di provare a farlo manualmente:

 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>)info.GetValue("transforms", typeof(List>)); AlignedMean = info.GetValue("am", typeof(double[])) as double[]; PointProfiles = (Dictionary, IPointTrainingSet>)info.GetValue("pps", typeof(Dictionary, IPointTrainingSet>)); FileNames = info.GetValue("fnames", typeof(string[])) as string[]; } 

Hai un grafo di oggetti molto complesso. Ho provato a rompere il costruttore di Profile e ProfileModel e ho avuto alcuni strani risultati, ma questa semplice versione sembra funzionare.