【问题标题】:Serializing cyclic object references using DataContractSerializer not working使用 DataContractSerializer 序列化循环对象引用不起作用
【发布时间】:2011-11-28 01:03:12
【问题描述】:

我正在构建一个 XNA 游戏,我正在尝试完全保存游戏/地图等状态,然后能够从完全相同的状态加载和恢复。

我的游戏逻辑包含相当复杂的元素(用于序列化),例如引用、委托等。我已经进行了数小时的研究并决定最好使用保留对象引用的DataContractSerializer。 (我也解决了委托,但这是另一个主题)我没有问题序列化和反序列化状态,正确和完整地重新创建对象、字段、列表甚至对象引用。但是我遇到了循环引用的问题。考虑这种情况:

class X{
   public X another;
}

//from code:
X first = new X();
X second = new X();
first.another = second;
second.another = first;

尝试序列化 X 将导致抱怨循环引用的异常。如果我注释掉最后一行,它工作正常。好吧,我可以想象为什么会这样,但我不知道如何解决它。我在某处读到我可以使用DataContract 属性并将IsReference 设置为true,但它对我没有任何改变——仍然出现错误。 (无论如何我都想避免它,因为我正在处理的代码是可移植代码,并且有一天也可能在 Xbox 上运行,而且 Xbox 的可移植库不支持 DataContract 所在的程序集。)

这是序列化的代码:

class DataContractContentWriterBase<T> where T : GameObject
{
    internal void Write(Stream output, T objectToWrite, Type[] extraTypes = null)
    {
        if (extraTypes == null) { extraTypes = new Type[0]; }
        DataContractSerializer serializer = new DataContractSerializer(typeof(T), extraTypes, int.MaxValue, false, true, null);
        serializer.WriteObject(output, objectToWrite);
    }
}

我正在从这个类中调用这段代码:

[ContentTypeWriter]
public class PlatformObjectTemplateWriter : ContentTypeWriter<TWrite>
(... lots of code ...)
    DataContractContentWriterBase<TWrite> writer = new DataContractContentWriterBase<TWrite>();
    protected override void Write(ContentWriter output, TWrite value)
    {
        writer.Write(output.BaseStream, value, GetExtraTypes());
    }

对于反序列化:

class DataContractContentReaderBase<T> where T: GameObject
{
    internal T Read(Stream input, Type[] extraTypes = null)
    {
        if (extraTypes == null) { extraTypes = new Type[0]; }
        DataContractSerializer serializer = new DataContractSerializer(typeof(T), extraTypes, int.MaxValue, false, true, null);
        T obj = serializer.ReadObject(input) as T;
        //return obj.Clone() as T; //clone falan.. bi bak iste.
        return obj;
    }
}

它被调用:

public class PlatformObjectTemplateReader : ContentTypeReader<TRead>
(lots of code...)
    DataContractContentReaderBase<TRead> reader = new DataContractContentReaderBase<TRead>();
    protected override TRead Read(ContentReader input, TRead existingInstance)
    {
        return reader.Read(input.BaseStream, GetExtraTypes());
    }

地点:

PlatformObjectTemplate 是我喜欢写的类型。

有什么建议吗?

解决方案:就在几分钟前,我意识到我没有用DataMember 属性标记字段,在我添加DataContract 属性之前,XNA 序列化程序是以某种方式充当“默认”序列化程序。现在,我已经标记了所有对象,现在一切正常。现在我的模型中有没有问题的循环引用。

【问题讨论】:

  • 如果您正在寻找替代序列化程序,protobuf-net(至少它的 v2)可能满足您的要求。
  • 谢谢,它看起来不错,但我需要一个可移植的类库兼容代码。
  • 你检查了吗?不兼容吗?
  • 在主页上,它指出,protobuf-net 是一个 .NET 实现,允许您高效轻松地序列化您的 .NET 对象。它与大多数 .NET 系列兼容,包括 .NET 2.0/3.0/3.5、.NET CF 2.0/3.5、Mono 2.x、Silverlight 2 等。 这些似乎不支持 PCL ,但是。
  • 仅仅因为它没有列出 PCL 并不意味着它不会工作。 PCL 本身旨在与那些非常相同的(Microsoft)框架兼容。因此,如果无法针对 PCL 成功构建 protobuf-net,我会感到惊讶。

标签: c# .net-4.0 xna portability datacontractserializer


【解决方案1】:

如果您不想使用[DataContract(IsReference=true)],那么DataContractSerializer 将无济于事,因为此属性可以解决引用问题的东西。

因此,您应该寻找替代的序列化程序,或者编写一些序列化代码,将您的图形转换为一些常规表示(如节点列表 + 它们之间的链接列表)并返回,然后序列化该简单结构。

如果您决定使用DataContract(IsReference=true),这里有一个序列化您的图表的示例:

[DataContract(IsReference = true)]
class X{
  [DataMember]
  public X another;
}

static void Main()
{
  //from code:
  var first = new X();
  var second = new X();
  first.another = second;
  second.another = first;

  byte[] data;
  using (var stream = new MemoryStream())
  {
    var serializer = new DataContractSerializer(typeof(X));

    serializer.WriteObject(stream, first);
    data = stream.ToArray();
  }
  var str = Encoding.UTF8.GetString(data2);
}

str 将包含以下 XML:

<X z:Id="i1" xmlns="http://schemas.datacontract.org/2004/07/GraphXmlSerialization"
   xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
  <another z:Id="i2">
    <another z:Ref="i1"/>
  </another>
</X>

【讨论】:

  • 如果它有效,我一定做错了什么..它应该如何使用?我将它输入到我的班级声明中,但它根本不起作用。
  • 如果您不应用[DataContract(IsReference=true)],序列化程序会将您的图形呈现为树:如果有多个对象引用单个对象,则后者将在输出中加倍。在相反的情况下,只有一个引用将渲染真实对象,而其余的将包含对第一个的特殊引用。您可能应该自己尝试一下。顺便说一句,这似乎可以很好地处理循环引用。
  • 我实际应用了,它适用于普通引用但在循环引用中抛出异常
  • 好吧,奇怪的是,当我深入研究我的异常时,我意识到它实际上抱怨 XNA 的序列化器属性,即使我使用自己的 DataContractSerializer 来实现它。这是我收到的错误消息:“{”在序列化 时发现循环引用您可能缺少 ContentSerializerAttribute.SharedResource 标志。”}”但我在我的程序集中没有对 XNA 的引用有我的对象,但我不会有它,而且我确定无论如何我都不需要它(我的对象在内部与 XNA 没有任何关系,因此未引用)
  • 嗯,这可能意味着您出于某种原因没有使用DataContractSerializer。请发布您用于序列化对象的代码(就像我发布的那样)。异常的堆栈跟踪也可能会有所帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-09-29
  • 1970-01-01
  • 2015-12-24
  • 1970-01-01
  • 1970-01-01
  • 2010-10-23
  • 2013-08-25
相关资源
最近更新 更多