使用带有CollectionDataContract的DataContractSerializer反序列化对象引用?

时间:2014-03-27 15:13:01

标签: c# datacontractserializer

一旦涉及对象引用,我似乎无法弄清楚如何正确使用CollectionDataContract属性。任何帮助是极大的赞赏。警告:代码示例可能看起来有点长,但我还没有找到解释这个问题的方法,所以请耐心等待。 (更新:今天我了解到foobar是邪恶的。我更正了,所以我用一个更有意义的例子替换了所有对“foo”和“bar”的引用。希望这有帮助。

考虑这个简单的flashcard界面,用于假想的测验应用程序:

interface IFlashcard
{
    string Question {get; set;}
    string Answer {get; set;}
}

它由类FlashcardFlippedFlashcard实现。 FlippedFlashcard的实例应该保留对Flashcard的相应实例的引用,以便反转QuestionAnswer,因为某些卡可能在“危险模式”下工作:

[DataContract]
class Flashcard : IFlashcard
{
    [DataMember]
    public string Question { get; set; }
    [DataMember]
    public string Answer { get; set; }
}

class FlippedFlashcard : IFlashcard
{
    [DataMember]
    public Flashcard OriginalFlashcard;

    [DataMember]
    public string Question
    {
        get { return OriginalFlashcard.Answer; }
        set { OriginalFlashcard.Answer = value; }
    }

    [DataMember]
    public string Answer
    {
        get { return OriginalFlashcard.Question; }
        set { OriginalFlashcard.Question = value; }
    }
}

让我们定义符合IFlashcard ...

的可序列化对象集合
[CollectionDataContract (ItemName = "Flashcard")]
class DeckOfFlashcards : List<IFlashcard>
{
}

...并填写一些抽认卡:

Flashcard f1 = new Flashcard() { Question = "What do you get when you multiply 6 by 7?", Answer = "42" };
FlippedFlashcard f2 = new FlippedFlashcard { OriginalFlashcard = f1 }; 
DeckOfFlashcards myDeck = new DeckOfFlashcards();
myDeck.Add(f1);
myDeck.Add(f2);
f1.Question = "What is the answer to life, the universe and everything?";
Console.WriteLine(f2.Answer); // >> What is the answer to life, the universe and everything?

到现在为止,一切都按照我希望的方式运作。当我更改f1中的Question时,“当你将6乘以7时会得到什么?” “生命,宇宙和一切的答案是什么?”,f2中的Answer正确反映了这一变化:

f1.Question = "What is the answer to life, the universe and everything?";
Console.WriteLine(f2.Answer); // >> What is the answer to life, the universe and everything?

我想序列化此集合并保留FlashcardFlippedFlashcard的实例之间的引用。这是我的尝试:

List<Type> types = new List<Type> { typeof(Flashcard), typeof(FlippedFlashcard) };
DataContractSerializer dcs = new DataContractSerializer(typeof(DeckOfFlashcards), types, 100, false, true, null);
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
using (XmlWriter writer = XmlWriter.Create("Flashcards.xml", settings))
    dcs.WriteObject(writer, myDeck); // >> Question, Answer and OriginalFlashcard from f2 are serialized with "i:nil="true"

不幸的是,生成的Xml文件将f2中的QuestionAnswerOriginalFlashcard列为空:

<?xml version="1.0" encoding="utf-8"?>
<DeckOfFlashcards xmlns:i="http://www.w3.org/2001/XMLSchema-instance" z:Id="1" z:Size="2" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" xmlns="http://schemas.datacontract.org/2004/07/Foobar">
  <Flashcard z:Id="2" i:type="Flashcard">
    <Answer z:Id="3">42</Answer>
    <Question z:Id="4">What is the answer to life, the universe and everything?</Question>
  </Flashcard>
  <Flashcard z:Id="5" i:type="FlippedFlashcard">
    <Answer z:Ref="4" i:nil="true" />
    <OriginalFlashcard z:Ref="2" i:nil="true" />
    <Question z:Ref="3" i:nil="true" />
  </Flashcard>
</DeckOfFlashcards>

当我尝试从文件中反序列化集合时......

DeckOfFlashcards myOtherDeck;
using (var reader = XmlReader.Create("Flashcards.xml"))
    myOtherDeck = (DeckOfFlashcards)dcs.ReadObject(reader); // >> fires NullReferenceException at "set { OriginalFoo.Bar2 = value; }" with value "What is the answer to life, the universe and everything?"

...我得到NullReferenceException,可能是FlippedFlashcard的Xml被击中。

有人有任何想法吗?

1 个答案:

答案 0 :(得分:1)

知道了!

为了防止NullPointerException,有必要检查[DataContract][DataMember]属性。 interface IFlashcard根本不需要合同:

interface IFlashcard
{
    string Question { get; set;  }
    string Answer { get; set; }
}

class Flashcard的代码保持不变,就像我在描述中写的一样。唯一真正需要做的更改是[DataMember] QuestionAnswer移除class FlippedFlashcard属性,如下所示:

[DataContract]
class FlippedFlashcard : IFlashcard
{
    [DataMember]
    public Flashcard OriginalFlashcard { get; set; }

    public string Question
    {
        get { return OriginalFlashcard.Answer; }
        set { OriginalFlashcard.Answer = value; }
    }

    public string Answer
    {
        get { return OriginalFlashcard.Question; }
        set { OriginalFlashcard.Question = value; }
    }
}

现在一切正常。 : - )