Сериализация циклических ссылок на объекты с использованием DataContractSerializer не работает - PullRequest
3 голосов
/ 28 ноября 2011

Я создаю игру 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 был каким-то образом действующий как сериализатор по умолчанию. Теперь я пометил все объекты, и теперь все работает отлично. Теперь у меня есть циклические ссылки без проблем в моей модели.

1 Ответ

2 голосов
/ 28 ноября 2011

Если вы не хотите использовать [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>
...