XMLSerialize和EXCLUDE基类

时间:2014-11-13 19:19:26

标签: xmlserializer

我希望能够对一个子类进行XML序列化,但是从序列化中排除它的基类。

这是我的类(为DataObjects.NET配置):

[Serializable]
[HierarchyRoot]
public class MyClass: Entity
{
    [Field, Key] public int Id { get; private set; }
    [Field(Length = 20)] public string Name { get; set; }
}

基类实体无法序列化,因为它没有无参数构造函数。

我不会试图通过反序列化来重构该对象(没有Liskov问题)。我只想要XML中子类的数据。 IXMLSerializable并不是一个有吸引力的选择,因为我的真正的课程数量众多且复杂得多。

有没有办法告诉XmlSerializer忽略基类?

1 个答案:

答案 0 :(得分:2)

首先,只要派生的MyClass有一个,基类就不需要无参数构造函数。因此,以下派生类可以从XML往返:

public class BaseClassWithNoParameterlessConstructor
{
    public BaseClassWithNoParameterlessConstructor(string value)
    {
        this.Value = value;
    }

    public string Value { get; set; }
}

public class DerivedClassWithAParameterlessConstructor : BaseClassWithNoParameterlessConstructor
{
    public DerivedClassWithAParameterlessConstructor()
        : this(string.Empty) // Some plausible default value
    {
    }

    public DerivedClassWithAParameterlessConstructor(string value)
        : base(value)
    {
    }

    public string Name { get; set; }
}

但是,属性public int Id { get; private set; }的存在将导致XmlSerializer构造函数抛出异常,因为Id无法公开设置。您需要使用[XmlIgnore]对其进行标记,或将setter设为公开。或者如果你真的不想让setter公开,并且从不打算反序列化你的xml,你可以做一些邪恶的事情:

    [XmlIgnore]
    public int Id { get; private set; }

    [XmlElement("Id")]
    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    public int DummyIdWIthFakeSetter { get { return Id; } set { /* DO NOTHING */ } }

其次,要抑制基类中的特定属性(例如,名为XXX),可以在派生类中引入名为bool ShouldSerializeXXX()的方法,并使其返回false。这将抑制该属性的输出,但需要逐个属性地完成。有关详细信息,请参阅here

如果要忽略在基类批发中声明的所有属性,可以使用给定construct an XmlSerializerXmlAttributeOverrides指定要忽略的基类属性名称,如here所示。但是,有一些问题需要注意:

  1. 您只能add XmlAttributes覆盖属性实际声明的类型的属性。这样做将适用于该类型和所有派生类型的属性。这将做你想要的 - 但可能比你想要的更多。如果尝试仅在派生类型中覆盖该属性,则忽略覆盖。我不确定这是否有记录,但我发现它是真的。因此,如果您序列化包含基类和派生类的对象图,并希望在" standalone"之后序列化基类属性。但是当子类化时,这种技术会产生不好的结果。但在你的情况下它应该没问题,因为你的基类不能单独序列化。

  2. 您必须在哈希表中显式缓存XmlSerializer以避免内存泄漏,如here所述。

  3. 因此,如果您知道应忽略其属性的所有基类型,则可以使用以下命令生成序列化器,该序列化器序列化子类并忽略基类属性:

    public static class XmlSerializationIgnoreOverrideCreator<T>
    {
        static Dictionary<HashSet<Type>, XmlSerializer> table = new Dictionary<HashSet<Type>, XmlSerializer>(HashSet<Type>.CreateSetComparer());
    
        public static XmlSerializer CreateOverrideSerializer()
        {
            return CreateOverrideSerializer(new Type[] { typeof(T).BaseType });
        }
    
        public static XmlSerializer CreateOverrideSerializer(IEnumerable<Type> typesWithPropertiesToIgnore)
        {
            var set = new HashSet<Type>(typesWithPropertiesToIgnore);
            if (set.Count == 0)
                return new XmlSerializer(typeof(T));
            lock (table)
            {
                XmlSerializer serializer;
                if (table.TryGetValue(set, out serializer))
                    return serializer;
    
                var xOver = new XmlAttributeOverrides();
    
                foreach (var type in set)
                {
                    IgnoreAllProperties(type, xOver);
                }
    
                table[set] = serializer = new XmlSerializer(typeof(T), xOver);
                return serializer;
            }
        }
    
        static void IgnoreAllProperties(Type type, XmlAttributeOverrides xOver)
        {
            if (type == null || type == typeof(object) || type.IsPrimitive || type == typeof(string))
                return;
            var attrs = new XmlAttributes() { XmlIgnore = true };
            foreach (var property in type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public))
                if (xOver[type, property.Name] == null) // Check to see if overrides for this base type were already set.
                    xOver.Add(type, property.Name, attrs);
            var baseType = type.BaseType;
            if (baseType != type)
                IgnoreAllProperties(baseType, xOver);
        }
    }