开发者

How to deserialize object with custom serializable properties?

I'm trying to serialize/deserialize collection of interfaces, which basically is unsupported. I found a question on SO where a proposition is stated to provide a serializable wrapper properties for those properties which depends on interfaces. So that's what I have:

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 {
    s开发者_如何学Ctring 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;}
}

Everything works fine if the A interface does not include "bad properties" (and of course they do not appear in the AImpl class). If it does than the collection's element does not deserialize and stops after deserializing first property of interface B.

Here are the wrappers:

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
}

and here is a method generating data for tests:

    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};
        }
    }
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜