我正在尝试序列化/反序列化接口集合,这基本上是不受支持的。我在SO上找到了question,其中提出了一个命题,为那些依赖于接口的属性提供可序列化的包装器属性。这就是我所拥有的:
public class Serialize {
public Serialize() {
this.SCollection = new SerializableInterfacesCollection<ObservableCollection<A>>(new ObservableCollection<A>());
}
[XmlIgnore()]
public ObservableCollection<A> Collection {
get {
return this.SCollection.Value;
}
set {
SCollection.Value = value;
}
}
public SerializableInterfacesCollection<ObservableCollection<A>> SCollection { get; set; }
}
public interface A {
string Str { get; set; }
int Int { get; set; }
// bad properties... very bad:
B B { get; set; }
ObservableCollection<B> Collection {get;set;}
}
public interface B {
string Label { get; set; }
string Value { get; set; }
}
public class AImpl: A {
public AImpl() {
SB = new SerializableInterface<B>();
SCollection = new SerializableInterfacesCollection<ObservableCollection<B>>(new ObservableCollection<B>());
}
[XmlAttribute()]
public string Type = typeof(AImpl).FullName;
public string Str {get;set;}
public int Int {get;set;}
[XmlIgnore()]
public B B {
get {
return SB.Value;
}
set {
SB.Value = value;
}
}
public SerializableInterface<B> SB;
[XmlIgnore()]
public ObservableCollection<B> Collection {
get {
return SCollection.Value;
}
set {
SCollection.Value = value;
}
}
public SerializableInterfacesCollection<ObservableCollection<B>> SCollection { get; set; }
}
public class BImpl01: B {
[XmlAttribute()]
public string Type = typeof(BImpl01).FullName;
public string Label {get;set;}
public string Value {get;set;}
}
public class BImpl02: B {
[XmlAttribute()]
public string Type = typeof(BImpl02).FullName;
public string Label {get;set;}
public string Value {get;set;}
}
如果A接口不包含“坏属性”(当然它们不出现在AImpl类中),一切正常。如果是,则集合的元素不反序列化并在反序列化接口B的第一个属性后停止。
以下是包装器:
public class SerializableInterface<T>: IXmlSerializable {
public SerializableInterface(T value) {
Value = value;
}
public SerializableInterface() { }
public T Value { get; set; }
public const string TypeAttr = "Type";
public const string NullAttrValue = "null";
#region IXmlSerializable Members
public System.Xml.Schema.XmlSchema GetSchema() {
return null;
}
public virtual void ReadXml(System.Xml.XmlReader reader) {
if (!reader.HasAttributes)
throw new FormatException("expected a type attribute!");
string type = reader.GetAttribute(TypeAttr);
reader.Read(); // consume the value
if (type == NullAttrValue)
return;// leave T at default value
XmlSerializer serializer = new XmlSerializer(Type.GetType(type));
this.Value = (T)serializer.Deserialize(reader);
}
public virtual void WriteXml(System.Xml.XmlWriter writer) {
if (Value == null) {
writer.WriteAttributeString(TypeAttr, NullAttrValue);
return;
}
try {
var type = Value.GetType();
var ser = new XmlSerializer(type);
writer.WriteAttributeString(TypeAttr, type.FullName);
ser.Serialize(writer, Value);
}
catch (Exception e) {
// some logging
throw e;
}
}
#endregion
}
public class SerializableInterfacesCollection<T>: SerializableInterface<T> where T: IList, new() {
public SerializableInterfacesCollection() { }
public SerializableInterfacesCollection(T value)
: base(value) {}
#region IXmlSerializable Members
public override void ReadXml(System.Xml.XmlReader reader) {
try {
if (!reader.HasAttributes)
throw new FormatException("No " + TypeAttr + " in element");
string type = reader.GetAttribute(TypeAttr);
if (string.IsNullOrEmpty(type) || type == NullAttrValue || type != typeof(T).FullName)
return;
reader.Read();
Value = new T();
while (reader.NodeType != XmlNodeType.EndElement) {
string typename = reader.GetAttribute(TypeAttr);
if (string.IsNullOrEmpty(typename)) {
throw new FormatException("Collection element has to have " + TypeAttr + " attribute specifing its type");
}
Type t = Type.GetType(typename);
if (null == t.GetInterface(typeof(T).GetProperty("Item").PropertyType.FullName))
break;
XmlSerializer xs = new XmlSerializer(t);
var o = xs.Deserialize(reader);
Value.Add(o);
}
}
catch (Exception e) {
// some logging
throw e;
}
}
public override void WriteXml(System.Xml.XmlWriter writer) {
if (Value == null) {
writer.WriteAttributeString(TypeAttr, NullAttrValue);
return;
}
try {
writer.WriteAttributeString(TypeAttr, Value.GetType().FullName);
foreach (var el in Value) {
var ser = new XmlSerializer(el.GetType());
ser.Serialize(writer, el);
}
}
catch (Exception e) {
// some logging
throw e;
}
}
#endregion
}
这是一个为测试生成数据的方法:
private Serialize makeA() {
Serialize result = new Serialize();
for (int i = 0; i < 10; ++i) {
A a = new AImpl() { Str = "str " + i, Int = i, B = makeB(i) };
for (int j = 0; j < 10; ++j) {
a.Collection.Add(makeB(j));
}
result.Collection.Add(a);
}
return result;
}
B makeB(int i) {
if (i % 2 == 0) {
return new BImpl01(){Label= "Blabel " + i, Value="value b"+i};
}
else {
return new BImpl02(){Label= "B2label " + i, Value="value b2"+i};
}
}