使用DataContractSerializer序列化循环对象引用无效



我正在构建一款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设置为真,但它没有改变任何东西对我来说-仍然得到了错误。(无论如何我想避免它,因为我正在编写的代码是可移植代码,可能有一天也会在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序列化器以某种方式充当"默认"序列化器。现在,我已经标记了所有的对象,现在一切都很顺利。我现在在我的模型中有循环引用,没有问题。

如果您不想使用[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>

最新更新