mark
mark

Reputation: 62784

Problems with DataContractSerializer - how to correctly serialize objects deriving from List<T>?

Observe the following sample code:

namespace A
{
  [Serializable]
  internal class ComplexObject<T> : List<T>, IEquatable<ComplexObject<T>>
    where T : IEquatable<T>
  {
    private T m_state;

    internal T State
    {
      get { return m_state; }
      set { m_state = value; }
    }

    public bool Equals(ComplexObject<T> other)
    {
      // Implementation is omitted to save space.
    }
  }

  public static class Program
  {
    public static void Main()
    {
      var obj = new ComplexObject<int>();
      obj.State = 100;
      var stream = new MemoryStream();
      var serializer = new DataContractSerializer(obj.GetType());
      serializer.WriteObject(stream, obj);
      stream.Flush();
      stream.Seek(0, SeekOrigin.Begin);
      var copy = (ComplexObject<int>)serializer.ReadObject(stream);
      Debug.Assert(obj.Equals(copy));
    }
  }
}

Note that ComplexObject<T> derives from List<T>.

Anyway, the last assertion fails. Replacing [Serializable] with [CollectionDataContract] and attaching [DataMember] to m_state yields the same negative result.

It is as though the DataContractSerializer notices that the class is a collection and chooses to ignore its other state.

Please advice anyone how to solve this issue given that:

Thanks a lot in advance.

EDIT:

public bool Equals(ComplexObject<T> other)
{
  if (!m_state.Equals(other.m_state) || Count != other.Count)
  {
    return false;
  }

  bool result = true;
  for (int i = 0; i < Count && (result = this[i].Equals(other[i])); ++i)
  {
  }
  return result;
}

Upvotes: 0

Views: 6254

Answers (3)

KenL
KenL

Reputation: 875

The problem is when you are trying to return an array of your object - at least it was for me.

I figure out that I needed to create a List of the type of my object class, add that list to the DataContractSerrializer(typeof(mylist));

thus;

List<LinqtoSQLTableClass> mylist = new List<LinqtoSQLTableClass>();
DataContractSerializer(mylist.GetType());
StringBuilder sb = new StringBuilder();

var query = linqtosql blah blah

XmlWriter writer = XmlWriter.Create(sb);
            dcs.WriteObject(writer, query);        
            writer.Close();

Upvotes: 1

enderminh
enderminh

Reputation: 166

To correctly serialize a List structure, you have to use the CollectionDataContract attribute like so:

 [CollectionDataContract]
 [Serializable]
 internal class ComplexObject<T> : List<T>, IEquatable<ComplexObject<T>>
    where T : IEquatable<T> 

However, the CollectionDataContract doesn't allow for additional DataMembers to be serialized. The workaround would be to avoid inheriting from the list, but make it a member variable instead and optionally implement the ICollection, like so:

[DataContract]
[Serializable]
internal class ComplexObject<T> : ICollection<T>, IEquatable<ComplexObject<T>>
  where T : IEquatable<T> 
{
    private T m_state;

    [DataMember]
    public T State
    {
        get { return m_state; }
        set { m_state = value; }
    }

    private List<T> m_List = new List<T>();

    [DataMember]
    public List<T> List
    {
        get { return m_List; }
        set { m_List = value; }
    }

    public bool Equals(ComplexObject<T> other)
    {
        if (!other.State.Equals(State))
            return false;

        if (other.List.Count != List.Count)
            return false;

        for (int i = 0; i < other.List.Count;i++)
        {
            if (!other.List[i].Equals(List[i]))
                return false;
        }

        return true;
    }

    // ICollection members left out to save space

    // helper methods to wrap around the List to decrease the amount
    // of refactoring work you would have to do
    public void Add(T item)
    {
        List.Add(item);
    }

    public bool Remove(T item)
    {
        return List.Remove(item);
    }

    public T this[int index]
    {
        get { return List[index]; }
    }
}

Upvotes: 2

Randy Minder
Randy Minder

Reputation: 48432

Here is some code I have for cloning or serializing an object. I would be curious to see if you have the same issues with this. This code simply returns an Object type, but you can cast the result to your object type.

var serializer = new System.Runtime.Serialization.DataContractSerializer(GetType());
using (var ms = new System.IO.MemoryStream())
{
   serializer.WriteObject(ms, this);
   ms.Position = 0;
   return serializer.ReadObject(ms);
}

Upvotes: -1

Related Questions