如何序列化类层次结构的类是指每个其他,但避免XmlInclude? [英] How to serialize class hierarchies whose classes refer to each to other, but avoiding XmlInclude?

查看:104
本文介绍了如何序列化类层次结构的类是指每个其他,但避免XmlInclude?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有,我想使用的XmlSerializer 类及其相关属性的序列化的类层次结构。有一个基抽象类,然后不少派生类(下面我code,我已经派生类的数量减少到5,但也有在实际code等等)。这些类形成了一个等级,并经常包含在层次结构中引用的类的实例。

I have a hierarchy of classes that I want to serialize using the XmlSerializer class and its related attributes. There is a base abstract class and then quite a few derived classes (in my code below, I've reduced the number of derived classes to five, but there are many more in the actual code). The classes form a hierarchy and frequently contain references to instances of classes in the hierarchy.

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

正如你所看到的,在我的code相当多的重复。在某些时候,一个新的派生类可以引入,并且所有的地方引用碱基类型被用于将具有的地方被重新访问了用新的XmlElement 属性。这是繁琐和容易出错。我想EX preSS这样一个事实: BASETYPE 可以反序列化作为Af - Ag型如果元素名称为T,但作为p型,如果元素名称为P,等等,只有一次。

As you can see, there is quite a bit of repetition in my code. At some point, a new derived class may be introduced, and all of the places where references to BaseType are used will have to be re-visited with a new XmlElement attribute. This is tedious as well as error-prone. I'd like to express the fact that a BaseType can be deserialized as a TType if the element name is "t", but as PType if the element name is "p", etc, exactly once.

我知道 XmlIncludeAttribute ,但它引入了 XSI:键入属性其中的金老板是不满意。有没有什么办法可以分解出的XML元素的名称和CLR类型之间的映射的知识呢?

I'm aware of XmlIncludeAttribute but it introduces xsi:type attributes which the "gold-owner" is not happy with. Is there any way to factor out the knowledge of the mapping between XML element names and CLR types?

一个假设的解决方案可以使的是,全套派生类中被定义 BASETYPE 组装已知的。这意味着我们不必考虑外部组件添加新的类混进去。

One assumption a solution can make is that the full set of derived classes is known by the assembly that defines BaseType. That means we don't have to consider external assemblies adding new classes into the mix.

推荐答案

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

After fiddling around with this for a while, I came up with a solution and am posting here to help others in the same situation.

首先,找到在类型层次中的所有类型。然后写一个函数,将生成一个包含所有这些类型的 XmlElements 例如:

First, locate all the types in the type hierarchy. Then write a function that builds an XmlElements instance containing all those types:

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

现在找到的所有类型的要序列化上面的类型之一的领域。你可以做到这一点,反映了所有类在装配的,但对我来说我碰巧知道有只有几类,有这样的要求:

Now locate all types that have fields of one of the types above that you wish to serialize. You could do this by reflecting all of the classes in the assembly, but in my case I happen to know that there are only a few classes that have this requirement:

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

我们现在通过创建 XmlAttributeOverrides 的实例进行了指定的相应类型的每个字段的 hierarchyTypeElements 覆盖

We now proceed by creating an instance of XmlAttributeOverrides that specifies the hierarchyTypeElements overrides for each field of the appropriate types:

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

(是的,我是滥用总结;的LinQ就是这样一个整洁的黄金锤)

(Yeah, I'm abusing Aggregate; LinQ is such a neat golden hammer )

最后,使用 XmlAttributeOverrides 实例来创建你的序列化:

Finally use the XmlAttributeOverrides instance to create your serializer:

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

您可能要缓存的串行器在一个静态变量,以避免泄露组件。

You may want to cache that serializer in a static variable to avoid leaking assemblies.

此code可以推广到也装饰性能以及字段。这将作为练习留给读者。

This code can be generalized to also decorate the properties as well as the fields. This is left as an exercise to the reader.

这篇关于如何序列化类层次结构的类是指每个其他,但避免XmlInclude?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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