XMLSerialize 和 EXCLUDE 基类 [英] XMLSerialize and EXCLUDE the base class

查看:23
本文介绍了XMLSerialize 和 EXCLUDE 基类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望能够对子类进行 XML 序列化,但从序列化中排除其基类.

I would like to be able to XML serialize a subclass, but exclude its base class from the serialization.

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

Here's my class (configured for DataObjects.NET):

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

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

Base class Entity cannot be serialized because it does not have a parameterless constructor.

我不会试图通过反序列化来重构对象(没有 Liskov 问题).我只想要来自 XML 子类的数据.IXMLSerializable 不是一个有吸引力的选择,因为我的真实类很多而且复杂得多.

I won't be trying to reconstitute the object by deserializing (no Liskov issues). I just want the data from the subclass in XML. IXMLSerializable is not an attractive option, as my real classes are numerous and much more complex.

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

Is there any way to tell XmlSerializer to ignore the base class?

推荐答案

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

First off, the base class doesn't need a parameterless constructor as long as the derived class, MyClass, has one. Thus the following derived class can be round-tripped from and to 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;私人订制;} 将导致 XmlSerializer 构造函数抛出异常,因为 Id 不能公开设置.您需要用 [XmlIgnore] 或将 setter 设为公开.或者,如果您真的不想将 setter 公开,并且从不打算反序列化您的 xml,那么您可以做一些邪恶的事情,例如:

However, the presence of the property public int Id { get; private set; } will cause the XmlSerializer constructor to throw an exception, because Id cannot be set publicly. You need to mark it with [XmlIgnore] or make the setter public. Or if you really don't want to make the setter public, and never intend to deserialize your xml, you could do something evil like:

    [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.这将抑制该属性的输出,但需要逐个属性完成.有关详细信息,请参阅此处.

Secondly, to suppress a specific property in a base class, named XXX for instance, you can introduce a method named bool ShouldSerializeXXX() in your derived class, and make it return false. This will suppress output of that property, but will need to be done property-by-property. See here for details.

如果您想批量忽略基类中声明的所有属性,您可以使用给定的 XmlAttributeOverrides 指定要忽略的基类属性名称,如图所示 此处.但是,有一些问题需要注意:

If you want to ignore all properties declared in base classes wholesale, you can construct an XmlSerializer with given XmlAttributeOverrides that specify base class property names to ignore, as is shown here. However, there are some issues of which to be aware:

  1. 您只能添加 一个 XmlAttributes 覆盖到实际声明属性的类型中的属性.这样做将应用于该类型和所有派生类型中的该属性.这会做你想做的 - 但可能比你想要的更多.如果您尝试仅在派生类型中覆盖该属性,则覆盖将被忽略.我不确定这是否被记录在案,但我发现这是真的.因此,如果您正在序列化包含基类和派生类的对象图,并希望在独立"时序列化基类属性而不是在子类化时序列化,则此技术将产生不良结果.但是在您的情况下应该没问题,因为无论如何您的基类都不能被单独序列化.

  1. You can only add an XmlAttributes override to a property in the type in which the property is actually declared. Doing so will apply to that property in that type and all derived types. This will do what you want - but maybe more than you want. If you try to override the property just in a derived type, the override is ignored. I'm not sure this is documented, but I have found it to be true. Thus if you were serializing an object graph containing both base and derived classes, and wanted to serialize base class properties when "standalone" but not when subclassed, this technique would produce bad results. But in your case it should be OK because your base classes cannot be serialized standalone anyway.

您必须在哈希表中明确缓存您的 XmlSerializer 以避免内存泄漏,正如 这里.

You must explicitly cache your XmlSerializer in a hash table to avoid memory leaks, as is explained here.

因此,如果您知道应忽略其属性的所有基类型,则可以使用以下内容来制造序列化子类并忽略基类属性的序列化程序:

Thus, if you know all the base types whose properties should be ignored, you can use the following to manufacture a serializer that serializes subclasses and ignores base class properties:

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

这篇关于XMLSerialize 和 EXCLUDE 基类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆