How to deserialize object with custom serializable properties?

青春壹個敷衍的年華 提交于 2019-12-11 05:16:07

问题


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

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

来源:https://stackoverflow.com/questions/7584922/how-to-deserialize-object-with-custom-serializable-properties

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!