【问题标题】:How to do reference fixup during deserialization in C#?如何在 C# 反序列化期间进行引用修复?
【发布时间】:2016-10-23 00:11:36
【问题描述】:

如何使用 C# 序列化框架进行引用修复(后处理)?

我有一个对象图,其中的对象引用其他对象。它们都实现了ISerializable 接口并且它们都有实例ID,因此在序列化状态下表示引用很容易。

关键在于,当调用反序列化构造函数时,该对象引用的所有对象可能都没有被反序列化,因此无法将引用设置为有效对象。而且我找不到任何方法可以连接到 C# 序列化框架中的后处理步骤来进行参考修复。有办法吗?


根据要求,这是一个我认为突出问题的人为类。

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

这是(反)序列化代码:

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);

这个问题没有解决我的问题:.net XML Serialization - Storing Reference instead of Object Copy

我想使用 BinaryFormatter + ISerializable 框架进行序列化,而不是切换到 XmlFormater。

【问题讨论】:

  • 反序列化过程中需要解析id的代码是什么样子的?
  • 这适用于现有/遗留代码吗?如果没有并且您想使用 XML,我会查看 XmlSerializer。这比使用ISerializer 接口和XmlFormatter 更容易使用并且需要更少的代码。
  • 如果你觉得我的回答对你有帮助,你可以accept my answer。

标签: c# serialization deserialization


【解决方案1】:

为此目的有一个属​​性。

在要反序列化的任何对象中实现以下方法:

[OnDeserialized]
internal void OnDeserializedMethod(StreamingContext context) {

}

System.Runtime.Serialization 中还有一些属性可能会对您有所帮助。

编辑

我稍微修改了你的代码:

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

测试方法:

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。

This 帖子是另一个信息来源

EDIT 2(测试方法保持不变):

方法一(完全反序列化和序列化) ...

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

...

【讨论】:

  • 这有什么帮助?我需要一种方法在 所有 对象(示例中的所有小马)被反序列化后运行,而不仅仅是这个。
  • 你有没有试过在你的Pony-class 中实现这个方法?如果是,您就会知道它完全符合您的要求
  • 是的,我做到了。它被调用两次;每匹小马一次。它在同一个对象的反序列化构造函数之前运行。 IE。它不会在 Pony 对象创建后运行,也不能用于修复 BFF 属性指针。
  • @Anders 我已经更新了我的代码并试图解释我做了什么以及为什么
  • 感谢您抽出宝贵时间对此进行调查。不幸的是,我确实需要 ISerializable 接口来解决我的真正问题(但你是对的,这个人为的例子并不真正需要它)。
猜你喜欢
  • 2018-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多