gpt4 book ai didi

c# - 如何序列化其类相互引用的类层次结构,但避免 XmlInclude?

转载 作者:太空宇宙 更新时间:2023-11-03 11:07:10 24 4
gpt4 key购买 nike

我有一个类层次结构,我想使用 XmlSerializer 类及其相关属性对其进行序列化。有一个基本抽象类,然后是相当多的派生类(在我下面的代码中,我已经将派生类的数量减少到五个,但在实际代码中还有更多)。这些类形成一个层次结构,并且经常包含对层次结构中类实例的引用。

public abstract class BaseType 
{
// Only classes in my assembly can derive from this class
internal BaseType() { }
}

public sealed class TType : BaseType
{
[XmlText]
public string Name;
}

public sealed class PType : BaseType
{
[XmlElement("t", typeof(TType)]
[XmlElement("p", typeof(PType)]
[XmlElement("a", typeof(AType)]
[XmlElement("s", typeof(SType)]
[XmlElement("u", typeof(UType)]
public BaseType Child;
}

public sealed class SType : BaseType
{
[XmlElement("t", typeof(TType)]
[XmlElement("p", typeof(PType)]
[XmlElement("s", typeof(SType)]
[XmlElement("a", typeof(AType)]
[XmlElement("u", typeof(UType)]
public BaseType [] Items;
public string [] ItemNames;
}

public sealed class AType : BaseType
{
[XmlElement("t", typeof(TType)]
[XmlElement("p", typeof(PType)]
[XmlElement("s", typeof(SType)]
[XmlElement("a", typeof(AType)]
[XmlElement("u", typeof(UType)]
public BaseType Item;
public int Length;
}

public sealed class UType : BaseType
{
[XmlElement("t", typeof(TType)]
[XmlElement("p", typeof(PType)]
[XmlElement("s", typeof(SType)]
[XmlElement("a", typeof(AType)]
[XmlElement("u", typeof(UType)]
public BaseType [] Alts;
public string [] AltNames;
}

最后,一个容器来容纳它们并提供给 XmlSerializer:

[XmlRoot("items")]
public class ItemCollection
{
[XmlElement("t", typeof(TType)]
[XmlElement("p", typeof(PType)]
[XmlElement("s", typeof(SType)]
[XmlElement("a", typeof(AType)]
[XmlElement("u", typeof(UType)]
public BaseType [] Items;
}

如您所见,我的代码中有相当多的重复。在某些时候,可能会引入一个新的派生类,并且必须使用新的 XmlElement 属性重新访问所有使用到 BaseType 的引用的地方。这既乏味又容易出错。我想表达这样一个事实,即如果元素名称为“t”,则 BaseType 可以反序列化为 TType,但如果元素名称为“p”,则反序列化为 PType,等等,恰好一次。

我知道 XmlIncludeAttribute 但它引入了 xsi:type 属性,“金主”对此并不满意。有什么方法可以分解出 XML 元素名称和 CLR 类型之间的映射知识吗?

解决方案可以做出的一个假设是,定义 BaseType 的程序集已知完整的派生类集。这意味着我们不必考虑将新类添加到组合中的外部程序集。

最佳答案

在摆弄了一段时间之后,我想出了一个解决方案并在这里发帖以帮助处于相同情况的其他人。

首先,定位类型层次结构中的所有类型。然后编写一个函数来构建包含所有这些类型的 XmlElements 实例:

XmlAttributes CreateXmlAttributesForHierarchyTypes()
{
return XmlAttributes
{
XmlElements =
{
new XmlElementAttribute("t", typeof (TType)),
new XmlElementAttribute("p", typeof (PType)),
new XmlElementAttribute("s", typeof (SType)),
new XmlElementAttribute("a", typeof (AType)),
new XmlElementAttribute("u", typeof (UType)),
}
};
}

现在找到所有具有您希望序列化的上述类型之一的字段的类型。您可以通过反射(reflect)程序集中的所有类来做到这一点,但就我而言,我碰巧知道只有少数几个类具有此要求:

Type [] typesToOverride = new Type[] 
{
typeof(PType),
typeof(SType),
typeof(AType),
typeof(UType),
typeof(ItemCollection),
};

我们现在继续创建 XmlAttributeOverrides 的实例,该实例为适当类型的每个字段指定 hierarchyTypeElements 覆盖:

public static XmlAttributeOverrides GetAttributeOverrides(IEnumerable<Type> typesToOverride)
{
var overrides = typesToOverride
.SelectMany(x => x.GetFields()) // Get a flat list of fields from all the types
.Where(f => f.FieldType == typeof (BaseType)) // Must have the right type
.Select(f => new
{
Field = f,
Attributes = GetXmlAttributes(f)
})
.Where(f => f.Attributes != null)
.Aggregate(
new XmlAttributeOverrides(),
(ov, field) =>
{
ov.Add(field.Field.DeclaringType, field.Field.Name, field.Attributes);
return ov;
});
return overrides;
}

(是的,我在滥用 Aggregate;LinQ 真是一把利落的金锤子)

最后使用 XmlAttributeOverrides 实例来创建序列化程序:

var attrOverrides = GetAttributeOverrides(TypesToDecorate);
serializer = new XmlSerializer(typeof(ItemCollection), attrOverrides);

您可能希望将该序列化器缓存在静态变量中以避免程序集泄漏。

此代码可以概括为还装饰属性和字段。这是留给读者的练习。

关于c# - 如何序列化其类相互引用的类层次结构,但避免 XmlInclude?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15455786/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com