我希望能够对一个子类进行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忽略基类?
答案 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 XmlSerializer
的XmlAttributeOverrides
指定要忽略的基类属性名称,如here所示。但是,有一些问题需要注意:
您只能add XmlAttributes
覆盖属性实际声明的类型的属性。这样做将适用于该类型和所有派生类型的属性。这将做你想要的 - 但可能比你想要的更多。如果尝试仅在派生类型中覆盖该属性,则忽略覆盖。我不确定这是否有记录,但我发现它是真的。因此,如果您序列化包含基类和派生类的对象图,并希望在" standalone"之后序列化基类属性。但是当子类化时,这种技术会产生不好的结果。但在你的情况下它应该没问题,因为你的基类不能单独序列化。
您必须在哈希表中显式缓存XmlSerializer
以避免内存泄漏,如here所述。
因此,如果您知道应忽略其属性的所有基类型,则可以使用以下命令生成序列化器,该序列化器序列化子类并忽略基类属性:
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);
}
}