如何使用自定义可序列化属性反序列化对象?

时间:2011-09-28 14:34:43

标签: c# xml-serialization deserialization

我正在尝试序列化/反序列化接口集合,这基本上是不受支持的。我在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};
        }
    }

0 个答案:

没有答案