Question

I can't seem to figure out how to use the CollectionDataContract attribute properly as soon as object references are involved. Any help is greatly appreciated. Warning: The code example might seem a little long, but I haven't found a way to explain this any shorter, so please bear with me. (Update: Today I learned that foobar is evil. I stand corrected, so I have replaced all references to "foo" and "bar" with a more meaningful example. Hope this helps.)

Consider this simple flashcard interface for a hypothetical quiz app:

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

It is implemented by classes Flashcard and FlippedFlashcard. Instances of FlippedFlashcard are supposed to keep a reference to a corresponding instance of Flashcard in order to reverse Question and Answer as some of the cards may work in "Jeopardy mode":

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

Let's define a serializable collection of objects conforming to IFlashcard...

[CollectionDataContract (ItemName = "Flashcard")]
class DeckOfFlashcards : List<IFlashcard>
{
}

...and fill it with some flashcards:

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?

Until now, everything works as I want it to work. When I change the Question in f1 from "What do you get when you multiply 6 by 7?" to "What is the answer to life, the universe and everything?", the Answer in f2 reflects this change correctly:

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?

I want to serialize this collection and keep the references between instances of Flashcard and FlippedFlashcard. Here is my attempt:

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"

Unfortunately, the resulting Xml file lists Question, Answer and OriginalFlashcard from f2 as empty:

<?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>

When I try to deserialize the collection from the file...

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?"

...I get a NullReferenceException, probably when the Xml for the FlippedFlashcard is hit.

Does anybody have any idea?

Was it helpful?

Solution

Got it!

In order to prevent the NullPointerException, it was necessary to review the [DataContract] and [DataMember] attributes. interface IFlashcard needs no contract at all:

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

The code for class Flashcard remains unchanged, just as I wrote it in the description. The only real change necessary was to remove the [DataMember]attributes from Question and Answer in class FlippedFlashcard, like this:

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

Now everything works fine. :-)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top