5

I have this class:

using System;
using System.Collections.Generic;
using System.Runtime.Serialization;

namespace Grouping
{
    [Serializable]
    public class Group<T> : HashSet<T>
    {
        public Group(string name)
        {
            this.name = name;
        }

        protected Group(){}

        protected Group(SerializationInfo info, StreamingContext context):base(info,context)
        {
            name = info.GetString("koosnaampje");
        }

        public override void GetObjectData(SerializationInfo info,StreamingContext context)
        {
            base.GetObjectData(info,context);
            info.AddValue("koosnaampje", Name);
        }

        private string name;
        public string Name
        {
            get { return name; }
            private set { name = value; }
        }
    }
}

As it inherits from HashSet it has to implement ISerializable, hence the protected constructor and GetObjectData method. Formerly I serialized and deserialized this class succesfully with the BinaryFormatter.

Because I want to be able to inspect the output that is generated by the serializer I want to switch to the DataContractSerializer.

I wrote this test:

[TestMethod]
public void SerializeTest()
{
    var group = new Group<int>("ints"){1,2,3};
    var serializer = new DataContractSerializer(typeof (Group<int>));
    using (var stream=File.OpenWrite("group1.xml"))
    {
        serializer.WriteObject(stream,group);
    }
    using (var stream=File.OpenRead("group1.xml"))
    {
        group = serializer.ReadObject(stream) as Group<int>;
    }
    Assert.IsTrue(group.Contains(1));
    Assert.AreEqual("ints",group.Name);
}

The test fails because the Name property is null! (the integers are (de)serialized correctly though) What is happening?

EDIT: it has nothing to do with the name backing field being private. Making it public has the same result.

Dabblernl
  • 15,831
  • 18
  • 96
  • 148
  • Why would the data contract serializer serialize that? There's no `[DataContract]` or `[DataMember]` attributes. – John Saunders Aug 16 '09 at 18:58
  • @John: I tried that of course. But I got an exception stating that: Type 'Grouping.Group`1[[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]' cannot be ISerializable and have DataContractAttribute attribute.. I thought that the DataContractSerializer would be perfectly happy with classes that are marked with the SerializableAttribute. – Dabblernl Aug 16 '09 at 19:27
  • @John - DCS does do that now... it used to be pure "I demand a contract", but now it will also infer one if missing. A corruption, IMO. It works like BinaryFormatter - i.e. it looks at the **fields**; which is quite easily the worst possible way it could have been done. But I'm not bitter. – Marc Gravell Aug 16 '09 at 19:38
  • @Marc: an abomination. Almost as bad as Web Site Projects. – John Saunders Aug 16 '09 at 19:45
  • See also: [XMLSerialize a custom collection](http://stackoverflow.com/questions/377486/xmlserialize-a-custom-collection "XMLSerialize a custom collection") – roomaroo Aug 17 '09 at 16:17

1 Answers1

5

This is nothing to do with ISerializable; DataContractSerializer simply doesn't use ISerializable (it will use IXmlSerializable, but you don't want to do that...)

Most serializers, including XmlSerializer and DataContractSerializer (and data-binding, for that matter), treat collections as different to entities. It can be one or the other, but not both. Because it detects that it is a "collection", it serializes the contents (i.e. whatever is in the set), not the properties (Name etc).

You should encapsulate a collection, rather than inherit it.

Also; to correctly use DataContractSerializer, it would be wise to add the [DataMember]/[DataContract] attributes. For example:

[Serializable, DataContract] // probably don't need [Serializable]
public class Group<T>
{
    [DataMember]
    public HashSet<T> Items { get; private set; }

    protected Group()
    {
        Items = new HashSet<T>();
    }
    public Group(string name) : this()
    {
        Name = name;
    }
    [DataMember]
    public string Name {get ;private set;}
}
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Thanks, the contents of the inherited collection were indeed correctly serialized, but not the members of the inheriting class. I will adjust the code to show that. – Dabblernl Aug 16 '09 at 19:53