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).
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);
}
}
}