Search code examples
c#serializationdatacontract

Serialization of class derived from List<> using DataContract


I'm trying to serialize a class derived from List<> using DataContract. The problem is that properties of my class won't get serialized.

My classes:

[CollectionDataContract] /*[Serializable]*/ /*[DataContract]*/
public class DownloadRuleCollection : List<DownloadRule> {

    [DataMember(EmitDefaultValue = false)]
    public string SomeProperty { get; set; }
    //this is, in fact, more complex, but this is enough for the example
}

[DataContract]
public class DownloadRule {
    [DataMember(EmitDefaultValue = false)]
    public string Name { get; set; }

    /*
     more properties
     ...
     */
}

Test:

static void Main(string[] args) {

    //fill test collection with some data...
    var col = new DownloadRuleCollection { SomeProperty = "someText" };

    var rule = new DownloadRule { Name = "test01" };
    col.Add(rule);

    rule = new DownloadRule { Name = "test02" };
    col.Add(rule);

    rule = new DownloadRule { Name = "test03" };
    col.Add(rule);

    //serialize
    Console.WriteLine("serializing");

    Serialize(col, "serializationTest.xml");

    Console.WriteLine("serialized");
    Console.ReadLine();
}

result:

<?xml version="1.0" encoding="utf-8"?>
<ArrayOfDownloadRule xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/ConsoleApplication1">
  <DownloadRule>
    <Name>test01</Name>
  </DownloadRule>
  <DownloadRule>
    <Name>test02</Name>
  </DownloadRule>
  <DownloadRule>
    <Name>test03</Name>
  </DownloadRule>
</ArrayOfDownloadRule>

As you can see, the List's items are serialized (and deserialized) properly, but the List itself does not get serialized. I have tried to use different attributes:
[Serializable], no change;
[DataContract], throws exception during serialization (collections cant use this attribute)

btw I am serializing also private fields, so I cannot use XmlSerializer (or other classes that can't serialize private fields).


Solution

  • Ok, so Climber104's solution would work, but I would need to re-implement all of the List's methods, which makes me feel that I am reinventing the wheel.

    JaredPar from Jarek Waliszko's thread suggests to use a wrapper class.
    The easiest is to use it just for the sake of serialization process, so I used an protected innner wrapper class. This allows me to achieve my goals with just a few lines of code needed.

    public class DownloadRuleCollection : List<DownloadRule> {
    
        public string SomeProperty { get; set; }
    
        public void Serialize(string fileName) {
            Serializer.Serialize(
                new DownloadRuleCollection_SerializationWrapper {
                    Collection = this,
                    SomeProperty = SomeProperty
                }, fileName);
        }
    
        public static DownloadRuleCollection Deserialize(string fileName) {
            var wrapper = Serializer.Deserialize<DownloadRuleCollection_SerializationWrapper>(fileName);
    
            var result = wrapper.Collection;
            result.SomeProperty = wrapper.SomeProperty;
    
            return result;
        }
    
        [DataContract(Name = "DownloadRuleCollection")]
        private class DownloadRuleCollection_SerializationWrapper {
    
            [DataMember(EmitDefaultValue = false, Name = "SomeProperty", Order = 0)]
            public string SomeProperty { get; set; }
    
            [DataMember(EmitDefaultValue = false, Name = "DownloadRules", Order = 1)]
            public DownloadRuleCollection Collection;
    
        }
    }
    
    [DataContract]
    public class DownloadRule {
        [DataMember(EmitDefaultValue = false)]
        public string Name { get; set; }
    }
    
    public static class Serializer {
        public static void Serialize<T>(T obj, string fileName) {
            using(XmlWriter writer = XmlWriter.Create(fileName, new XmlWriterSettings { Indent = true }))
                new DataContractSerializer(typeof(T)).WriteObject(writer, obj);
        }
    
        public static T Deserialize<T>(Stream stream) {
            return (T)new DataContractSerializer(typeof(T)).ReadObject(stream);
        }
    
        public static T Deserialize<T>(string fileName) {
            using(FileStream fs = File.OpenRead(fileName)) {
                return Deserialize<T>(fs);
            }
        }
    }