1

I wrote a generic List type. How do I make the List serializable? My main concern is with the GetObjectData() method.

The following is my List class with its methods. I have done XML serialization before with the List<> that is already available in C# but I just don't know what I should write in the GetObjectData() method. Some form of foreach loop maybe?

class Liste<T> : IEnumerable, ISerializable
{
    List_Element<T> First;
    List_Element<T> Last;
    int position = -1;
    public T this[int index]
    {
        get
        {
            return GetAt(index);
        }
        set
        {
            //Person p = new Person();
            //InsertAt(index, p);
        }
    }
    public bool MoveNext()
    {
        position++;
        if (currentElement == null)
        {
            currentElement = First;
        }
        else
        {
            currentElement = currentElement.Next;
        }
        return (position < Count);
    }
    internal bool MovePrev()
    {
        position--;
        if (currentElement == null)
        {
            currentElement = Last;
        }
        else
        {
            currentElement = currentElement.Prev;
        }
        return currentElement != null;
    }
    private List_Element<T> currentElement;
    public T Current
    {
        get
        {
            return currentElement.Data;
        }
    }
    public void Add(T data)
    {
        List_Element<T> listelement = new List_Element<T>();
        listelement.Data = data;
        if (First == null)
        {
            First = listelement;
            Last = listelement;
        }
        else
        {
            Last.Next = listelement;
            listelement.Prev = Last;
            Last = listelement;
        }
        Count++;
    }
    public int Count { get; private set; }
    public void NeueListe()
    {
        First = null;
    }
    public void InsertEnd(T data)
    {
        List_Element<T> ende = new List_Element<T>();
        ende.Data = data;
        if (First == null)
        {
            First = ende;
            Last = ende;
        }
        else
        {
            Last.Next = ende;
            ende.Prev = Last.Prev;
            Last = ende;
            ende.Prev.Prev = Last.Prev.Prev;
        }
        Count++;
    }
    public void Reset()
    {
        position = -1;
    }
    public T GetAt(int index)
    {
        List_Element<T> laufvariable;
        laufvariable = First;
        for (int i = 0; i < index; i++)
        {
            laufvariable = laufvariable.Next;
        }
        if (index == 0)
        {
            return laufvariable.Data;
        }
        else
        {
            return laufvariable.Data;
        }
    }
    public void InsertAt(int index, T data)
    {
        List_Element<T> insertElement = new List_Element<T>();
        insertElement.Data = data;
        if (index == 0)
        {
            insertElement.Next = First;
            First = insertElement;
        }
        else
        {
            List_Element<T> laufVariable;
            laufVariable = First;
            for (int i = 0; i < index - 1; i++)
            {
                laufVariable = laufVariable.Next;
            }
            insertElement.Next = laufVariable.Next;
            laufVariable.Next = insertElement;
            insertElement.Prev = laufVariable;
        }
        if (insertElement.Next == null)
        {
            Last = insertElement;
        }
        else
        {
            insertElement.Next.Prev = insertElement;
        }
    }
    public void RemoveAt(int index)
    {
        if (index == 0)
        {
            if (Count == 1)
            {
                First = null;
                Last = null;
                Count--;
                return;
            }
            //First = First.Next;
            //First.Prev = null;
            else
            {
                First = First.Next;
                First.Prev = null;
                Count--;
            }
        }
        else
        {
            List_Element<T> laufvariable;

            laufvariable = First;
            for (int i = 0; i < index - 1; i++)
            {
                laufvariable = laufvariable.Next;
            }
            if (laufvariable.Next.Next == null)
            {
                Last = laufvariable;
                laufvariable.Next = null;
            }
            else
            {
                laufvariable.Next = laufvariable.Next.Next;
                laufvariable.Next.Prev = laufvariable;
            }
            Count--;
        }
    }
    public void Removelast(int index)
    {
        List_Element<T> laufvariable;
        laufvariable = First;
        for (int i = 0; i < index - 1; i++)
        {
            laufvariable = laufvariable.Next;
        }
        laufvariable = null;
    }
    public IEnumerator GetEnumerator()
    {
        if (First == null)
        {
            yield break;
        }

        if (First == Last)
        {                
            yield return First;
            MoveNext();
        }

        MoveNext();

        while (currentElement != null)
        {           
            yield return currentElement;
            MoveNext();
        }

        if (currentElement == null)
        {
            Reset();
            yield break;
        }

    }

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        // What to do here?
    }
dbc
  • 104,963
  • 20
  • 228
  • 340
  • Start from [here](https://stackoverflow.com/q/21692193/1997232). Otherwise there should be no problem to serialize 2 properties of `List`, it's not clear what is the issue. – Sinatr Jan 09 '18 at 09:59
  • What properties are you serializing? There are no People or Addresses in your class. You the properties that get serialize must be PUBLIC. – jdweng Jan 09 '18 at 10:05
  • I have a Seperate Class called "Person" and "Adress" I link those two together in another Class, I Display them in a ListViewTable. Everything worked when i used the regular List provided in C#, I have a UI with Save and Load Buttons, etc. – Blopsrusher Jan 09 '18 at 10:14
  • 2
    You have included the xml tag; does that mean you're trying to serialize *as xml*? because `GetObjectData` is unrelated to xml. If this is for `BinaryFormatter`, then: *why* are you using `BinaryFormatter`? (it isn't usually a great choice, and there are much better alternatives - "which is most appropriate" depends on the *why* in that sentence). You mention "semester" - if the "why" here is because your tutor somehow thinks that `BinaryFormatter` is "the done thing", then feel free to put your tutor in contact with me so I can update them on the state of modern .net serialization choices. – Marc Gravell Jan 09 '18 at 10:29
  • Are you trying to tell me that i don't need the GetObjectData for XML Serialization? My Tutor told me i would need to implement it somehow and i should google etc. Can i Serialize It without the GetObjectData Method? Im Trying to use XML Thanks for your Answer – Blopsrusher Jan 09 '18 at 11:52
  • Im Trying with IXmlSerializable right now. – Blopsrusher Jan 09 '18 at 12:04
  • @Blopsrusher - what serializer are you using? `XmlSerializer`? – dbc Jan 09 '18 at 18:33
  • Yes XmlSerializer, i got it working with the non generic version now. – Blopsrusher Jan 12 '18 at 12:00

1 Answers1

0

Modifying your question a bit, you are asking:

I have a custom generic collection that implements IEnumerable. How can I serialize it to XML in .Net?

.Net has two XML serializers, XmlSerializer and DataContractSerializer. Both have special rules for serializing collections that are documented in the following locations:

Despite the fact that these serializers do not share a code base, their rules are very similar:

  1. The type should implement IEnumerable<T> as well as IEnumerable, so that the serializer can determine the expected element type.

  2. The type must have a parameterless constructor.

  3. The type must have a public Add(T item) method whose single argument is assignable from the generic type T of the generic enumerator.

  4. The type cannot implement IEnumerable<T> multiple times for multiple types T.

  5. XmlSerializer only: the type (and generic item type) must be public.

  6. The collection items are serialized not the collection properties. XmlSerializer will never serialize collection properties. DataContractSerializer will serialize collection properties only when the collection is marked with [DataContract], in which case the properties are serialized instead of the items rather than in addition to the items.

By following these rules you can avoid implementing IXmlSerializable entirely, which is tricky and prone to subtle errors.

Thus if I simplify your Liste<T> greatly, the following implementation can be serialized by both serializers:

public class Liste<T> : IEnumerable<T>
{
    readonly List<T> underlyingList = new List<T>();

    public void Add(T item)
    {
        underlyingList.Add(item);
    }

    #region IEnumerable<T> Members

    public IEnumerator<T> GetEnumerator()
    {
        return underlyingList.GetEnumerator();
    }

    #endregion

    #region IEnumerable Members

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    #endregion
}

And if I define a List<int> as follows:

var list = new Liste<int>
{
    1, 2, 3, 4,
};

Both serializers will generate the following XML:

<ArrayOfInt>
  <int>1</int>
  <int>2</int>
  <int>3</int>
  <int>4</int>
</ArrayOfInt>

Sample working .Net fiddle for XmlSerializer.

As noted in comments, ISerializable is not even used by XmlSerializer. It is supported by DataContractSerializer as documented here -- but serializing using the standard pattern above will be easier.

dbc
  • 104,963
  • 20
  • 228
  • 340