How to do link binding during deserialization in C #?

How can I bind links (post-processing) using a C # serialization scheme?

I have a graph of objects with objects referencing other objects. They all implement the interface ISerializable, and they all have instance identifiers, so representing links in a serialized state is easy.

The trick is that when you call the deserialization constructor, all the objects referenced by this object may not be deserialized, so links cannot be set to valid objects. And I can’t find any way to connect to the post-processing step in the C # serialization structure to perform a reference fix. Is there any way to do this?


As requested, here is a clever class that seems to me to emphasize the problem.

[Serializable]
public class Pony : ISerializable
{
  public int Id { get; set; }
  public string Name { get; set; }
  public Pony BFF { get; set; }

  public Pony() {}
  private Pony(SerializationInfo info, StreamingContext context) {
    Id = info.GetInt32("id");
    Name = info.GetString("name");
    var bffId = info.GetInt32("BFFId");
    BFF = null; // <===== Fixup! Find Pony instance with id == bffId
  }

  public void GetObjectData(SerializationInfo info, StreamingContext ontext) {
    info.AddValue("id", Id);
    info.AddValue("name", Name);
    info.AddValue("BFFId", BFF.Id);
  }
}

And here is the serialization code (de):

var rd = new Pony { Id = 1, Name = "Rainbow Dash" };
var fs = new Pony { Id = 2, Name = "Fluttershy", BFF = rd };
rd.BFF = fs;
var ponies = new List<Pony>{ rd, fs };
Stream stream = new MemoryStream();
var formatter = new BinaryFormatter();
formatter.Serialize(stream, ponies);
stream.Seek(0, SeekOrigin.Begin);
var deserializedPonies = (List<Pony>)formatter.Deserialize(stream);

This question does not solve my problem: .net XML serialization - saving a link instead of copying objects

I would like to use the BinaryFormatter + ISerializable structure for serialization and not switch to XmlFormater.

+4
source share
1 answer

There is an attribute for this purpose.

Implement the following method on any object that you want to deserialize:

[OnDeserialized]
internal void OnDeserializedMethod(StreamingContext context) {

}

There System.Runtime.Serializationare a few more attributes that can help you.

EDIT

I changed the code a bit:

[Serializable]
  public class Pony  {
    public int Id {
      get; set;
    }
    public string Name {
      get; set;
    }
    public Pony BFF {
      get; set;
    }

    public Pony() {
    }

    [OnDeserialized]
    internal void OnDeserializedMethod(StreamingContext context) {
      Console.WriteLine(this.Id + " " + this.Name + " " + this.BFF?.Name);
    }
  }

TestMethod:

var rd = new Pony { Id = 1, Name = "Rainbow Dash" };
      var fs = new Pony { Id = 2, Name = "Fluttershy", BFF = rd };
      rd.BFF = fs;
      var ponies = new List<Pony> { rd, fs };

      object returnValue;
      using (var memoryStream = new MemoryStream()) {
        var binaryFormatter = new BinaryFormatter();
        binaryFormatter.Serialize(memoryStream, ponies);
        memoryStream.Position = 0;
        returnValue = binaryFormatter.Deserialize(memoryStream);
      }
      var xx = (List<Pony>)returnValue;

, ISerializable, GetObjectData - .

, , , , (De) Serialization.

EDIT 2 ( ):

One ( ) ...

    private Pony(SerializationInfo info, StreamingContext context) {

      foreach (SerializationEntry entry in info) {
        switch (entry.Name) {
          case "Id":
            this.Id = (int)entry.Value;
            break;
          case "Name":
            this.Name = (string)entry.Value;
            break;
          case "BFF":
            this.BFF = (Pony)entry.Value;
            break;
        }
      }
    }

    public void GetObjectData(SerializationInfo info, StreamingContext ontext) {
      info.AddValue("Id", Id);
      info.AddValue("Name", Name);
      info.AddValue("BFF", BFF);
    }
  }

...

2 ( - Id):

...

private Pony(SerializationInfo info, StreamingContext context) {

      foreach (SerializationEntry entry in info) {
        switch (entry.Name) {
          case "Id":
            this.Id = (int)entry.Value;
            break;
          case "Name":
            this.Name = (string)entry.Value;
            break;
          case "BFF.Id":
            var bffId = (int)entry.Value;
            this.BFF = GetPonyById(bffId); // You have to implement this
            break;
        }
      }
    }

    public void GetObjectData(SerializationInfo info, StreamingContext ontext) {
      info.AddValue("Id", Id);
      info.AddValue("Name", Name);
      info.AddValue("BFF.Id", BFF.Id);
    }

...

+1

All Articles